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.
*/
#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", "<level>", NULL },
.help = "Change maximum level of logged messages",
},
[CMD_JAIL_STATUS] = {
}, {
.type = CMD_JAIL_STATUS,
.argc = 1, .tokenc = 3,
.tokens = { "jail", "<jailname>", "status", NULL },
.help = "Show status and stats of given jail",
},
[CMD_JAIL_SET] = {
}, {
.type = CMD_JAIL_SET,
.argc = 3, .tokenc = 5,
.tokens = { "jail", "<jailname>", "set", "<param>", "<value>", NULL },
.help = "Set parameter of given jail",
},
[CMD_JAIL_IP_STATUS] = {
}, {
.type = CMD_JAIL_IP_STATUS,
.argc = 2, .tokenc = 5,
.tokens = { "jail", "<jailname>", "ip", "status", "<ip>", NULL },
.help = "Show ip status in given jail",
},
[CMD_JAIL_IP_BAN] = {
}, {
.type = CMD_JAIL_IP_BAN,
.argc = 2, .tokenc = 5,
.tokens = { "jail", "<jailname>", "ip", "ban", "<ip>", NULL },
.help = "Forcefully ban some ip in given jail",
},
[CMD_JAIL_IP_RELEASE] = {
}, {
.type = CMD_JAIL_IP_RELEASE,
.argc = 2, .tokenc = 5,
.tokens = { "jail", "<jailname>", "ip", "release", "<ip>", NULL },
.help = "Forcefully release some ip in given jail",
},
[CMD_JAIL_FILTER_STATS] = {
}, {
.type = CMD_JAIL_FILTER_STATS,
.argc = 1, .tokenc = 4,
.tokens = { "jail", "<jailname>", "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", "<jailname>", "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;
}

71
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_ */

2
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")

61
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;
}

Loading…
Cancel
Save