Browse Source

* refactor f2b_cmd_t

master
Alex 'AdUser' Z 3 years ago
parent
commit
830ed21375
  1. 235
      src/commands.c
  2. 71
      src/commands.h
  3. 2
      t/CMakeLists.txt
  4. 61
      t/t_cmd.c

235
src/commands.c

@ -5,211 +5,182 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include "common.h" #include "common.h"
#include "buf.h"
#include "commands.h" #include "commands.h"
struct f2b_cmd_t { struct cmd_desc {
enum f2b_command_type type;
const short int argc; const short int argc;
const short int tokenc; const short int tokenc;
const char *help; const char *help;
const char *tokens[CMD_TOKENS_MAX]; const char *tokens[CMD_TOKENS_MAXCOUNT];
} commands[CMD_MAX_NUMBER] = { } commands[] = {
[CMD_NONE] = { {
.argc = 0, .tokenc = 0, .type = CMD_HELP,
.tokens = { NULL },
.help = "Unspecified command"
},
[CMD_RESP] = {
.argc = 1, .tokenc = 0,
.tokens = { NULL },
.help = "Command response, used internally",
},
[CMD_HELP] = {
.argc = 0, .tokenc = 1, .argc = 0, .tokenc = 1,
.tokens = { "help", NULL }, .tokens = { "help", NULL },
.help = "Show available commands", .help = "Show available commands",
}, }, {
[CMD_PING] = { .type = CMD_STATUS,
.argc = 0, .tokenc = 1,
.tokens = { "ping", NULL },
.help = "Check the connection",
},
[CMD_STATUS] = {
.argc = 0, .tokenc = 1, .argc = 0, .tokenc = 1,
.tokens = { "status", NULL }, .tokens = { "status", NULL },
.help = "Show general stats and jails list", .help = "Show general stats and jails list",
}, }, {
[CMD_LOG_ROTATE] = { .type = CMD_RELOAD,
.argc = 0, .tokenc = 2,
.tokens = { "log", "rotate", NULL },
.help = "Reopen daemon's own log file",
},
[CMD_RELOAD] = {
.argc = 0, .tokenc = 1, .argc = 0, .tokenc = 1,
.tokens = { "reload", NULL }, .tokens = { "reload", NULL },
.help = "Reload own config, all jails will be reset.", .help = "Reload own config, all jails will be reset.",
}, }, {
[CMD_SHUTDOWN] = { .type = CMD_SHUTDOWN,
.argc = 0, .tokenc = 1, .argc = 0, .tokenc = 1,
.tokens = { "shutdown", NULL }, .tokens = { "shutdown", NULL },
.help = "Gracefully terminate f2b daemon", .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, .argc = 1, .tokenc = 3,
.tokens = { "log", "level", "<level>", NULL }, .tokens = { "log", "level", "<level>", NULL },
.help = "Change maximum level of logged messages", .help = "Change maximum level of logged messages",
}, }, {
[CMD_JAIL_STATUS] = { .type = CMD_JAIL_STATUS,
.argc = 1, .tokenc = 3, .argc = 1, .tokenc = 3,
.tokens = { "jail", "<jailname>", "status", NULL }, .tokens = { "jail", "<jailname>", "status", NULL },
.help = "Show status and stats of given jail", .help = "Show status and stats of given jail",
}, }, {
[CMD_JAIL_SET] = { .type = CMD_JAIL_SET,
.argc = 3, .tokenc = 5, .argc = 3, .tokenc = 5,
.tokens = { "jail", "<jailname>", "set", "<param>", "<value>", NULL }, .tokens = { "jail", "<jailname>", "set", "<param>", "<value>", NULL },
.help = "Set parameter of given jail", .help = "Set parameter of given jail",
}, }, {
[CMD_JAIL_IP_STATUS] = { .type = CMD_JAIL_IP_STATUS,
.argc = 2, .tokenc = 5, .argc = 2, .tokenc = 5,
.tokens = { "jail", "<jailname>", "ip", "status", "<ip>", NULL }, .tokens = { "jail", "<jailname>", "ip", "status", "<ip>", NULL },
.help = "Show ip status in given jail", .help = "Show ip status in given jail",
}, }, {
[CMD_JAIL_IP_BAN] = { .type = CMD_JAIL_IP_BAN,
.argc = 2, .tokenc = 5, .argc = 2, .tokenc = 5,
.tokens = { "jail", "<jailname>", "ip", "ban", "<ip>", NULL }, .tokens = { "jail", "<jailname>", "ip", "ban", "<ip>", NULL },
.help = "Forcefully ban some ip in given jail", .help = "Forcefully ban some ip in given jail",
}, }, {
[CMD_JAIL_IP_RELEASE] = { .type = CMD_JAIL_IP_RELEASE,
.argc = 2, .tokenc = 5, .argc = 2, .tokenc = 5,
.tokens = { "jail", "<jailname>", "ip", "release", "<ip>", NULL }, .tokens = { "jail", "<jailname>", "ip", "release", "<ip>", NULL },
.help = "Forcefully release some ip in given jail", .help = "Forcefully release some ip in given jail",
}, }, {
[CMD_JAIL_FILTER_STATS] = { .type = CMD_JAIL_FILTER_STATS,
.argc = 1, .tokenc = 4, .argc = 1, .tokenc = 4,
.tokens = { "jail", "<jailname>", "filter", "stats", NULL }, .tokens = { "jail", "<jailname>", "filter", "stats", NULL },
.help = "Show matches stats for jail regexps", .help = "Show matches stats for jail regexps",
}, }, {
[CMD_JAIL_FILTER_RELOAD] = { .type = CMD_JAIL_FILTER_RELOAD,
.argc = 1, .tokenc = 4, .argc = 1, .tokenc = 4,
.tokens = { "jail", "<jailname>", "filter", "reload", NULL }, .tokens = { "jail", "<jailname>", "filter", "reload", NULL },
.help = "Reload regexps for given jail", .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() { f2b_cmd_help() {
struct f2b_cmd_t *cmd = NULL; f2b_buf_t buf;
const char **p = NULL; const char **p = NULL;
fputs("Available commands:\n\n", stdout); if (help)
for (size_t i = CMD_PING; i < CMD_MAX_NUMBER; i++) { return help;
cmd = &commands[i];
if (cmd->tokens[0] == NULL) if (!f2b_buf_alloc(&buf, 8192))
continue; return "internal error: can't allocate memory\n";
for (p = cmd->tokens; *p != NULL; p++) f2b_buf_append(&buf, "Available commands:\n\n", 0);
fprintf(stdout, "%s ", *p); for (struct cmd_desc *cmd = commands; cmd->type != CMD_UNKNOWN; cmd++) {
fprintf(stdout, "\n\t%s\n\n", cmd->help); 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 bool
f2b_cmd_append_arg(char *buf, size_t bufsize, const char *arg) { f2b_cmd_parse(f2b_cmd_t *cmd, const char *src) {
assert(buf != NULL); char *p = NULL;
assert(arg != NULL);
strlcat(buf, arg, bufsize);
strlcat(buf, "\n", bufsize);
}
/** assert(cmd != NULL);
* @brief Parse command from line assert(src != NULL);
* @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(line != NULL);
/* strip leading spaces */ /* strip leading spaces */
while (isblank(*line)) while (isblank(*src))
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') f2b_buf_alloc(&cmd->data, strlen(src) + 1);
return CMD_NONE; /* empty string */ f2b_buf_append(&cmd->data, src, 0);
/* simple commands without args */ /* simple commands without args */
if (strcmp(line, "ping") == 0) { return CMD_PING; } cmd->argc = 1; /* we has at least one arg */
else if (strcmp(line, "help") == 0) { return CMD_HELP; } cmd->args[0] = cmd->data.data;
else if (strcmp(line, "status") == 0) { return CMD_STATUS; } if (strcmp(src, "help") == 0) { cmd->type = CMD_HELP; return true; }
else if (strcmp(line, "reload") == 0) { return CMD_RELOAD; } else if (strcmp(src, "status") == 0) { cmd->type = CMD_STATUS; return true; }
else if (strcmp(line, "shutdown") == 0) { return CMD_SHUTDOWN; } 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 */ /* split string to tokens */
tokenc = 1; /* we has at least one token */ p = cmd->data.data;
tokens[0] = strtok_r(line, " \t", &p); cmd->args[0] = strtok_r(cmd->data.data, " \t", &p);
for (size_t i = 1; i < CMD_TOKENS_MAX; i++) { for (size_t i = 1; i < CMD_TOKENS_MAXCOUNT; i++) {
tokens[i] = strtok_r(NULL, " \t", &p); cmd->args[i] = strtok_r(NULL, " \t", &p);
if (tokens[i] == NULL) if (cmd->args[i] == NULL)
break; break;
tokenc++; cmd->argc++;
} }
buf[0] = '\0'; if (strcmp(cmd->args[0], "jail") == 0 && cmd->argc > 1) {
if (strcmp(line, "jail") == 0 && tokenc > 1) {
/* commands for jail */ /* commands for jail */
f2b_cmd_append_arg(buf, bufsize, tokens[1]); if (cmd->argc == 3 && strcmp(cmd->args[2], "status") == 0) {
if (tokenc == 3 && strcmp(tokens[2], "status") == 0) { cmd->type = CMD_JAIL_STATUS; return true;
return CMD_JAIL_STATUS;
} }
if (tokenc == 5 && strcmp(tokens[2], "set") == 0) { if (cmd->argc == 5 && strcmp(cmd->args[2], "set") == 0) {
f2b_cmd_append_arg(buf, bufsize, tokens[3]); cmd->type = CMD_JAIL_SET; return true;
f2b_cmd_append_arg(buf, bufsize, tokens[4]);
return CMD_JAIL_SET;
} }
if (tokenc == 5 && strcmp(tokens[2], "ip") == 0 && strcmp(tokens[3], "status") == 0) { if (cmd->argc == 5 && strcmp(cmd->args[2], "ip") == 0 && strcmp(cmd->args[3], "status") == 0) {
f2b_cmd_append_arg(buf, bufsize, tokens[4]); cmd->type = CMD_JAIL_IP_STATUS; return true;
return CMD_JAIL_IP_STATUS;
} }
if (tokenc == 5 && strcmp(tokens[2], "ip") == 0 && strcmp(tokens[3], "ban") == 0) { if (cmd->argc == 5 && strcmp(cmd->args[2], "ip") == 0 && strcmp(cmd->args[3], "ban") == 0) {
f2b_cmd_append_arg(buf, bufsize, tokens[4]); cmd->type = CMD_JAIL_IP_BAN; return true;
return CMD_JAIL_IP_BAN;
} }
if (tokenc == 5 && strcmp(tokens[2], "ip") == 0 && strcmp(tokens[3], "release") == 0) { if (cmd->argc == 5 && strcmp(cmd->args[2], "ip") == 0 && strcmp(cmd->args[3], "release") == 0) {
f2b_cmd_append_arg(buf, bufsize, tokens[4]); cmd->type = CMD_JAIL_IP_RELEASE; return true;
return CMD_JAIL_IP_RELEASE;
} }
if (tokenc == 4 && strcmp(tokens[2], "filter") == 0 && strcmp(tokens[3], "stats") == 0) { if (cmd->argc == 4 && strcmp(cmd->args[2], "filter") == 0 && strcmp(cmd->args[3], "stats") == 0) {
return CMD_JAIL_FILTER_STATS; cmd->type = CMD_JAIL_FILTER_STATS; return true;
} }
if (tokenc == 4 && strcmp(tokens[2], "filter") == 0 && strcmp(tokens[3], "reload") == 0) { if (cmd->argc == 4 && strcmp(cmd->args[2], "filter") == 0 && strcmp(cmd->args[3], "reload") == 0) {
return CMD_JAIL_FILTER_RELOAD; cmd->type = CMD_JAIL_FILTER_RELOAD; return true;
} }
} else if (strcmp(line, "log") == 0 && tokenc > 1) { } else if (strcmp(cmd->args[0], "log") == 0 && cmd->argc > 1) {
if (tokenc == 2 && strcmp(tokens[1], "rotate") == 0) { if (cmd->argc == 2 && strcmp(cmd->args[1], "rotate") == 0) {
return CMD_LOG_ROTATE; cmd->type = CMD_LOG_ROTATE; return true;
} }
if (tokenc == 3 && strcmp(tokens[1], "level") == 0) { if (cmd->argc == 3 && strcmp(cmd->args[1], "level") == 0) {
f2b_cmd_append_arg(buf, bufsize, tokens[2]); cmd->type = CMD_LOG_LEVEL; return true;
return CMD_LOG_LEVEL;
} }
} }
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; return false;
} }

71
src/commands.h

@ -10,30 +10,23 @@
/** /**
* @file * @file
* This header contains definition of control commands and routines * This header contains definition of control commands and routines
* for work with data buffer of control message * for parsing user input
*/ */
/** #define CMD_TOKENS_MAXCOUNT 6 /**< Maximum count of data pieces in control message data buf */
* Maximum length of input line in client #define CMD_TOKENS_MAXSIZE 260 /**< parsed tokens */
* @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
/** control command type */ enum f2b_command_type {
enum f2b_cmd_type { CMD_UNKNOWN = 0, /**< unset */
CMD_NONE = 0, /**< unset */ CMD_HELP, /**< show help for commands */
CMD_RESP, /**< response of command */ CMD_STATUS, /**< show general status of f2b daemon */
CMD_HELP, /**< show help for commands (used internally by client) */ CMD_RELOAD, /**< reload all jails */
CMD_PING = 8, /**< check connection */ CMD_SHUTDOWN, /**< gracefull shutdown daemon */
CMD_STATUS, /**< show general status of f2b daemon */ /* logging */
CMD_LOG_ROTATE,/**< reopen logfile. works only if set `logdest = file` */ CMD_LOG_ROTATE, /**< reopen logfile. (only for `logdest = file`) */
CMD_RELOAD, /**< reload all jails */ CMD_LOG_LEVEL, /**< change maximum level of logged messages */
CMD_SHUTDOWN, /**< gracefull shutdown */
CMD_LOG_LEVEL, /**< change maximum level of logged messages */
/* jail commands */ /* 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_SET, /**< set parameter of given jail */
CMD_JAIL_IP_STATUS, /**< show status of given ip */ CMD_JAIL_IP_STATUS, /**< show status of given ip */
CMD_JAIL_IP_BAN, /**< force ban given ip */ CMD_JAIL_IP_BAN, /**< force ban given ip */
@ -43,36 +36,28 @@ enum f2b_cmd_type {
CMD_MAX_NUMBER, /**< placeholder */ 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 * @brief Try to parse user input
* @param buf Buffer of control message for storing parsed args * @param cmd pointer to preallocated f2b_cmd_t struct
* @param bufsize Size of buffer size above
* @param src Line with user input * @param src Line with user input
* @returns @a CMD_NONE if parsing fails, or cmd type less than @a CMD_MAX_NUMBER on success * @returns true on success, false otherwise
*/
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
*/ */
bool 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_ */ #endif /* F2B_COMMANDS_H_ */

2
t/CMakeLists.txt

@ -3,7 +3,7 @@ enable_testing()
set(SRC_DIR "../src") set(SRC_DIR "../src")
add_executable("t_buf" "t_buf.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/buf.c") 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_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_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") add_executable("t_ipaddr" "t_ipaddr.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/matches.c" "${SRC_DIR}/ipaddr.c")

61
t/t_cmd.c

@ -1,34 +1,41 @@
#include "../src/common.h" #include "../src/common.h"
#include "../src/buf.h"
#include "../src/commands.h" #include "../src/commands.h"
int main() { int main() {
char buf[1024]; f2b_cmd_t cmd;
const char *line;
assert(f2b_cmd_parse(&cmd, "status") == true);
UNUSED(line); assert(cmd.type = CMD_STATUS);
assert(cmd.argc == 1);
buf[0] = '\0'; assert(strcmp(cmd.args[0], "status") == 0);
f2b_cmd_append_arg(buf, sizeof(buf), "42"); assert(cmd.data.used == 6); /* "status" */
assert(strcmp(buf, "42\n") == 0); f2b_buf_free(&cmd.data);
line = "status"; assert(f2b_cmd_parse(&cmd, "stat") == false); /* no such command */
assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_STATUS); assert(cmd.type == CMD_UNKNOWN);
line = "statu"; /* no such command */ assert(f2b_cmd_parse(&cmd, "jail test") == false); /* incomplete command */
assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_NONE); assert(cmd.type == CMD_UNKNOWN);
line = "jail test"; /* incomplete command */ assert(f2b_cmd_parse(&cmd, "jail test status") == true);
assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_NONE); assert(cmd.type == CMD_JAIL_STATUS);
assert(cmd.argc == 3);
buf[0] = '\0'; assert(strcmp(cmd.args[0], "jail") == 0);
line = "jail test status"; assert(strcmp(cmd.args[1], "test") == 0);
assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_JAIL_STATUS); assert(strcmp(cmd.args[2], "status") == 0);
assert(strcmp(buf, "test\n") == 0); assert(cmd.data.used == 16); /* "jail\0test\0status" */
f2b_buf_free(&cmd.data);
buf[0] = '\0';
line = "jail test set bantime 7200"; assert(f2b_cmd_parse(&cmd, "jail test set bantime 7200") == true);
assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_JAIL_SET); assert(cmd.type == CMD_JAIL_SET);
assert(strcmp(buf, "test\nbantime\n7200\n") == 0); 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; return EXIT_SUCCESS;
} }

Loading…
Cancel
Save