You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
246 lines
4.5 KiB
246 lines
4.5 KiB
#include <assert.h> |
|
#include <string.h> |
|
#include <stdlib.h> |
|
#include <sys/types.h> |
|
#include <sys/wait.h> |
|
#include <unistd.h> |
|
|
|
#include "backend.h" |
|
|
|
#define ID_MAX 32 |
|
|
|
typedef struct cmd_t { |
|
struct cmd_t *next; |
|
char *args; /**< stores path of cmd & args, delimited by '\0' */ |
|
char **argv; /**< stores array of pointers to args + NULL */ |
|
size_t argc; /**< args count */ |
|
size_t pos; /**< index+1 in argv[] where to insert IP address (zero means "no placeholder") */ |
|
} cmd_t; |
|
|
|
struct _config { |
|
char name[ID_MAX + 1]; |
|
time_t timeout; |
|
cmd_t *start; |
|
cmd_t *stop; |
|
cmd_t *ban; |
|
cmd_t *unban; |
|
cmd_t *exists; |
|
}; |
|
|
|
static cmd_t * |
|
cmd_from_str(const char *str) { |
|
cmd_t *cmd = NULL; |
|
const char *delim = " \t"; |
|
char *token, *saveptr, **argv; |
|
|
|
assert(str != NULL); |
|
|
|
if ((cmd = calloc(1, sizeof(cmd_t))) == NULL) |
|
return NULL; |
|
|
|
if ((cmd->args = strdup(str)) == NULL) |
|
goto cleanup; |
|
|
|
cmd->argc = 1; |
|
if ((cmd->argv = calloc(2, sizeof(cmd->argv))) == NULL) |
|
goto cleanup; |
|
cmd->argv[cmd->argc] = NULL; |
|
|
|
strtok_r(cmd->args, delim, &saveptr); |
|
cmd->argv[0] = cmd->args; |
|
|
|
while ((token = strtok_r(NULL, delim, &saveptr)) != NULL) { |
|
if ((argv = realloc(cmd->argv, sizeof(cmd->argv) * (cmd->argc + 2))) == NULL) |
|
goto cleanup; |
|
cmd->argv = argv; |
|
if (strcmp(token, HOST_TOKEN) == 0) |
|
cmd->pos = cmd->argc; |
|
cmd->argv[cmd->argc] = token; |
|
cmd->argc++; |
|
} |
|
cmd->argv[cmd->argc] = NULL; |
|
|
|
return cmd; |
|
|
|
cleanup: |
|
free(cmd->args); |
|
free(cmd->argv); |
|
free(cmd); |
|
return NULL; |
|
} |
|
|
|
static cmd_t * |
|
cmd_list_append(cmd_t *list, cmd_t *cmd) { |
|
cmd_t *c = NULL; |
|
|
|
assert(cmd != NULL); |
|
|
|
if (!list) |
|
return cmd; |
|
|
|
for (c = list; c->next != NULL; c = c->next); |
|
|
|
c->next = cmd; |
|
return list; |
|
} |
|
|
|
static void |
|
cmd_list_destroy(cmd_t *list) { |
|
cmd_t *next = NULL; |
|
|
|
for (; list != NULL; list = next) { |
|
next = list->next; |
|
free(list->args); |
|
free(list->argv); |
|
free(list); |
|
} |
|
} |
|
|
|
static bool |
|
cmd_list_exec(cmd_t *list, const char *ip, time_t timeout) { |
|
int status = 0; |
|
pid_t pid; |
|
|
|
for (cmd_t *cmd = list; cmd != NULL; cmd = cmd->next) { |
|
pid = fork(); |
|
if (pid == 0) { |
|
/* child */ |
|
if (ip && cmd->pos) |
|
cmd->argv[cmd->pos - 1] = strdup(ip); |
|
if (timeout) |
|
alarm(timeout); |
|
execv(cmd->args, cmd->argv); |
|
} else if (pid > 0) { |
|
/* parent */ |
|
waitpid(pid, &status, 0); |
|
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) |
|
continue; |
|
return false; |
|
} else { |
|
/* parent, fork error */ |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
/* handlers */ |
|
cfg_t * |
|
create(const char *id) { |
|
cfg_t *cfg = NULL; |
|
|
|
assert(id != NULL); |
|
|
|
if ((cfg = calloc(1, sizeof(cfg_t))) == NULL) |
|
return NULL; |
|
strncpy(cfg->name, id, sizeof(cfg->name)); |
|
cfg->name[ID_MAX] = '\0'; |
|
|
|
return cfg; |
|
} |
|
|
|
#define CREATE_CMD(TYPE) \ |
|
if (strcmp(key, #TYPE) == 0) { \ |
|
cmd = cmd_from_str(value); \ |
|
if (cmd) { \ |
|
cfg->TYPE = cmd_list_append(cfg->TYPE, cmd); \ |
|
return true; \ |
|
} \ |
|
return false; \ |
|
} |
|
|
|
bool |
|
config(cfg_t *cfg, const char *key, const char *value) { |
|
cmd_t *cmd = NULL; |
|
|
|
assert(cfg != NULL); |
|
assert(key != NULL); |
|
assert(value != NULL); |
|
|
|
if (strcmp(key, "timeout") == 0) { |
|
cfg->timeout = atoi(value); |
|
return true; |
|
} |
|
|
|
CREATE_CMD(start) |
|
CREATE_CMD(stop) |
|
CREATE_CMD(ban) |
|
CREATE_CMD(unban) |
|
CREATE_CMD(exists) |
|
|
|
return false; |
|
} |
|
|
|
bool |
|
ready(cfg_t *cfg) { |
|
assert(cfg != NULL); |
|
|
|
if (cfg->ban && cfg->unban) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
bool |
|
start(cfg_t *cfg) { |
|
assert(cfg != NULL); |
|
|
|
if (!cfg->start) |
|
return true; |
|
|
|
return cmd_list_exec(cfg->start, NULL, cfg->timeout); |
|
} |
|
|
|
bool |
|
stop(cfg_t *cfg) { |
|
assert(cfg != NULL); |
|
|
|
if (!cfg->stop) |
|
return true; |
|
|
|
return cmd_list_exec(cfg->stop, NULL, cfg->timeout); |
|
} |
|
|
|
bool |
|
ban(cfg_t *cfg, const char *ip) { |
|
assert(cfg != NULL && ip != NULL); |
|
|
|
return cmd_list_exec(cfg->ban, ip, cfg->timeout); |
|
} |
|
|
|
bool |
|
unban(cfg_t *cfg, const char *ip) { |
|
assert(cfg != NULL && ip != NULL); |
|
|
|
return cmd_list_exec(cfg->unban, ip, cfg->timeout); |
|
} |
|
|
|
bool |
|
exists(cfg_t *cfg, const char *ip) { |
|
assert(cfg != NULL && ip != NULL); |
|
|
|
if (!cfg->exists) |
|
return true; |
|
|
|
return cmd_list_exec(cfg->exists, ip, cfg->timeout); |
|
} |
|
|
|
|
|
bool |
|
ping(cfg_t *cfg) { |
|
return cfg != NULL; |
|
} |
|
|
|
void |
|
destroy(cfg_t *cfg) { |
|
assert(cfg != NULL); |
|
|
|
/* free commands */ |
|
cmd_list_destroy(cfg->start); |
|
cmd_list_destroy(cfg->stop); |
|
cmd_list_destroy(cfg->ban); |
|
cmd_list_destroy(cfg->unban); |
|
cmd_list_destroy(cfg->exists); |
|
free(cfg); |
|
}
|
|
|