From: dhfelix Date: Fri, 8 Feb 2019 20:26:52 +0000 (-0600) Subject: Add flag parser framework X-Git-Tag: v0.0.2~87^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6f781bf9d61325ad4c3cf63eca838d06018773c4;p=thirdparty%2FFORT-validator.git Add flag parser framework - checkpoint commit - First version, stable and working but needs some tweaks --- diff --git a/src/Makefile.am b/src/Makefile.am index d615e8c5..fe0cf7fe 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -22,6 +22,7 @@ rpki_validator_SOURCES += state.h state.c rpki_validator_SOURCES += thread_var.h thread_var.c rpki_validator_SOURCES += uri.h uri.c rpki_validator_SOURCES += config.h config.c +rpki_validator_SOURCES += toml_handler.h toml_handler.c rpki_validator_SOURCES += rsync/rsync.h rsync/rsync.c diff --git a/src/config.c b/src/config.c index 2b8fcd21..df9d526d 100644 --- a/src/config.c +++ b/src/config.c @@ -1,135 +1,341 @@ #include "config.h" +#include #include -#include -#include -#include +#include +#include -#include - -#include "file.h" +#include "common.h" #include "log.h" -#include "thread_var.h" -#include "uri.h" +#define OPT_FIELD_ARRAY_LEN(array) ARRAY_LEN(array) - 1 -static void -print_config(struct rpki_config *config) +static int parse_bool(struct option_field *, char *, void *); + +struct args_flag { + struct option_field *field; + bool is_set; +}; + +static struct global_type gt_bool = { + .id = GTI_BOOL, + .name = "Boolean", + .size = sizeof(bool), + .parse = parse_bool, + .candidates = "true false", +}; + +static struct global_type gt_string = { + .id = GTI_STRING, + .name = "String", +}; + +static struct option_field global_fields[] = { + { + .name = "local-repository", + .type = >_string, + .doc = "Local repository path.", + .offset = offsetof(struct rpki_config, local_repository), + .has_arg = required_argument, + .short_opt = 0, + .required = true, + .candidates = "path", + }, { + .name = "enable-rsync", + .type = >_bool, + .doc = "Enable or disable rsync downloads.", + .offset = offsetof(struct rpki_config, enable_rsync), + .has_arg = optional_argument, + .short_opt = 0, + .required = false, + }, + { NULL }, +}; + +static struct option_field tal_fields[] = { + { + .name = "tal", + .type = >_string, + .doc = "TAL file path", + .offset = offsetof(struct rpki_config, tal), + .has_arg = required_argument, + .short_opt = 0, + .required = true, + .candidates = "file", + }, { + .name = "shuffle-uris", + .type = >_bool, + .doc = "Shuffle URIs in the TAL.", + .offset = offsetof(struct rpki_config, shuffle_uris), + .has_arg = optional_argument, + .short_opt = 0, + .required = false, + }, + { NULL }, +}; + +static int str_to_bool(const char *str, bool *bool_out) { - pr_debug("Program configuration"); - pr_debug_add("{"); - pr_debug("%s: %s", "local_repository", - config->local_repository); - pr_debug("%s: %s", "tal.file", config->tal); - pr_debug("%s: %s", "enable_rsync", - config->enable_rsync ? "true" : "false"); - pr_debug("%s: %s", "tal.shuffle_uris", - config->shuffle_uris ? "true" : "false"); - pr_debug_rm("}"); + if (strcasecmp(str, "true") == 0 || strcasecmp(str, "1") == 0 || + strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0) { + *bool_out = true; + return 0; + } + + if (strcasecmp(str, "false") == 0 || strcasecmp(str, "0") == 0 || + strcasecmp(str, "no") == 0 || strcasecmp(str, "off") == 0) { + *bool_out = false; + return 0; + } + + return pr_err("Cannot parse '%s' as a bool " + "(true|false|1|0|yes|no|on|off).", str); +} + +static int parse_bool(struct option_field *field, char *str, void *result) +{ + bool *value = result; + + switch (field->has_arg) { + case no_argument: + *value = true; + return 0; + break; + case required_argument: + case optional_argument: + if (field->has_arg == optional_argument && str == NULL) { + *value = true; + return 0; + } + /** + * XXX: (fine) GETOPT should had ensure that the code did + * not reach here for this particular case. + * */ + return str_to_bool(str, result); + break; + } + + if (str == NULL) { + *value = true; + return 0; + } + + return str_to_bool(str, result); } static int -handle_tal_table(struct toml_table_t *tal, struct rpki_config *config) +construct_options(struct args_flag **flags, struct option **long_options, + int *flags_len) { - const char *result; - int error, bool_result; + struct args_flag *result_flags; + struct option_field *global, *tal; + struct option *result_options; + unsigned int global_len, tal_len, total_len, i, result_idx; - result = toml_raw_in(tal, "file"); - if (result != 0) { - pr_debug("tal.file raw string %s", result); - error = toml_rtos(result, &config->tal); - if (error) - return pr_err("Bad value in '%s'", "file"); + get_global_fields(&global, &global_len); + get_tal_fields(&tal, &tal_len); + + total_len = global_len + tal_len; + + result_flags = calloc(total_len, sizeof(struct args_flag)); + if (result_flags == NULL) + return pr_enomem(); + + /* Long options must end with zeros (+1) */ + result_options = calloc(total_len + 1, sizeof(struct option)); + if (result_options == NULL) { + free(result_flags); + return pr_enomem(); } - result = toml_raw_in(tal, "shuffle_uris"); - if (result != 0) { - pr_debug("Boolean %s", result); + result_idx = 0; + for(i = 0; i < global_len; i++) { + result_flags[result_idx].field = &(global[i]); - error = toml_rtob(result, &bool_result); - if (error) - return pr_err("Bad value in '%s'", "shuffle_uris"); - config->shuffle_uris = bool_result; + result_options[result_idx].name = global[i].name; + result_options[result_idx].has_arg = global[i].has_arg; + result_options[result_idx].val = global[i].short_opt; + result_options[result_idx].flag = NULL; + + result_idx++; + } + + for(i = 0; i < tal_len; i++) { + result_flags[result_idx].field = &(tal[i]); + + result_options[result_idx].name = tal[i].name; + result_options[result_idx].has_arg = tal[i].has_arg; + result_options[result_idx].val = tal[i].short_opt; + result_options[result_idx].flag = NULL; + + result_idx++; } + *flags = result_flags; + *flags_len = total_len; + *long_options = result_options; + return 0; } -static int -toml_to_config(struct toml_table_t *root, struct rpki_config *config) +static void +set_string(void **field, char *str) +{ + *field = str; +} + +static void +set_config_param_for_string(void **field, void **config_param) { - struct toml_table_t *tal; - const char *result; - int error, bool_result; + *config_param = *field; +} - result = toml_raw_in(root, "local_repository"); - if (result != 0) { - error = toml_rtos(result, &config->local_repository); - if (error) - return pr_err("Bad value in '%s'", "local_repository"); - } +int +handle_option(struct rpki_config *config, struct option_field *field, char *str) +{ + void *config_param; + int error = 0; - result = toml_raw_in(root, "enable_rsync"); - if (result != 0) { - error = toml_rtob(result, &bool_result); - if (error) - return pr_err("Bad value in '%s'", "enable_rsync"); - config->enable_rsync = bool_result; + /** + * TODO Should we use a switch case? + * In order to avoid: + * warning: pointer of type ‘void *’ used in arithmetic + * [-Wpointer-arith] + * https://stackoverflow.com/questions/23357442/ + * dynamically-access-member-variable-of-a-structure + */ + config_param = config; + config_param += field->offset; + + if (field->type == >_string) { + set_string(config_param, str); + set_config_param_for_string(config_param, &config_param); + } else if (field->type->parse != NULL){ + error = field->type->parse(field, str, config_param); } + if (error) + return error; - tal = toml_table_in(root, "tal"); - if (tal != 0) - error = handle_tal_table(tal, config); - else - return pr_err("Required table '%s' is missing.", "tal"); + if (field->validate != NULL) + error = field->validate(field, config_param); + else if (field->type->validate != NULL) + error = field->type->validate(field, config_param); return error; } int -set_config_from_file(char *config_file, struct rpki_config *config) +check_missing_flags(struct args_flag *flag) { - struct file_contents fc; - struct toml_table_t *root; - struct rpki_uri uri; - char errbuf[200]; - int error; - bool is_config_file; - - /* I think I'm not using this struct correctly but I need it to call - * some functions, so be careful using the struct rpki_uri here. - * Also no needs to be freed. */ - uri.global = config_file; - uri.global_len = strlen(config_file); - uri.local = config_file; - - is_config_file = uri_has_extension(&uri, ".ini"); - is_config_file |= uri_has_extension(&uri, ".toml"); - if (!is_config_file) { - error = pr_err("Invalid Config file extension for file '%s'", - uri.local); - goto end; + char *candidate = NULL; + + if (flag->is_set) + return 0; + if (!flag->field->required) + return 0; + + printf("Missing flag --%s", flag->field->name); + switch (flag->field->has_arg) { + case no_argument: + break; + case optional_argument: + if (flag->field->candidates != NULL) + candidate = flag->field->candidates; + else if (flag->field->type->candidates != NULL) + candidate = flag->field->type->candidates; + if (candidate != NULL) + printf("[=%s]", candidate); + break; + case required_argument: + if (flag->field->candidates != NULL) + candidate = flag->field->candidates; + else if (flag->field->type->candidates != NULL) + candidate = flag->field->type->candidates; + if (candidate != NULL) + printf(" <%s>", candidate); + break; + default: + break; } - error = file_load(&uri, &fc); + printf("\n"); + + return -ENOENT; +} + +static void +print_config(struct rpki_config *config) +{ + pr_debug("Program configuration"); + pr_debug_add("{"); + pr_debug("%s: %s", "local-repository", config->local_repository); + pr_debug("%s: %s", "tal", config->tal); + pr_debug("%s: %s", "enable-rsync", + config->enable_rsync ? "true" : "false"); + pr_debug("%s: %s", "tal.shuffle-uris", + config->shuffle_uris ? "true" : "false"); + pr_debug_rm("}"); +} + +int +handle_flags_config(int argc, char **argv, struct rpki_config *config) +{ + struct args_flag *flags; + struct option *long_options; + int opt, indexptr, flags_len, error = 0; + + flags = NULL; + long_options = NULL; + config->flag_config = true; + + error = construct_options(&flags, &long_options, &flags_len); if (error) - goto end; /* Error msg already printed. */ + return error; /* Error msg already printed. */ - root = toml_parse((char *) fc.buffer, errbuf, sizeof(errbuf)); - file_free(&fc); + while ((opt = getopt_long(argc, argv, "", long_options, &indexptr)) + != -1) { + switch (opt) { + case 0: + flags[indexptr].is_set = true; + error = handle_option(config, flags[indexptr].field, + optarg); + break; + default: + error = pr_err("some usage hints.");/* TODO */ + break; + } - if (root == NULL) { - error = pr_err("Error while parsing configuration file: %s", - errbuf); - goto end; + if (error) + goto end; } - error = toml_to_config(root, config); - - toml_free(root); + for (indexptr = 0; indexptr < flags_len; indexptr++) { + error |= check_missing_flags(&flags[indexptr]); + } print_config(config); end: + free(flags); + free(long_options); return error; + +} + +void +get_global_fields(struct option_field **fields, unsigned int *len) +{ + if (fields) + *fields = global_fields; + if (len) + *len = OPT_FIELD_ARRAY_LEN(global_fields); +} + +void +get_tal_fields(struct option_field **fields, unsigned int *len) +{ + if (fields) + *fields = tal_fields; + if (len) + *len = OPT_FIELD_ARRAY_LEN(tal_fields); } diff --git a/src/config.h b/src/config.h index 6d760af0..eb4c01db 100644 --- a/src/config.h +++ b/src/config.h @@ -2,6 +2,7 @@ #define SRC_CONFIG_H_ #include +#include struct rpki_config { /* tal file path*/ @@ -16,6 +17,48 @@ struct rpki_config { bool flag_config; }; -int set_config_from_file(char *, struct rpki_config *); +typedef enum global_type_id { + GTI_BOOL, + GTI_STRING, +} global_type_id; + +struct option_field; + +typedef void (*print_function)(void *, bool); +typedef int (*parse_function)(struct option_field *, char *, void *); +/* This function does not need to validate type->size. */ +typedef int (*validate_function)(struct option_field *, void *); + +struct global_type { + global_type_id id; + const char *name; + size_t size; + print_function print; + parse_function parse; + validate_function validate; + char *candidates; +}; + +struct option_field { + char *name; /* This being NULL means the end of the array. */ + struct global_type *type; + const char *doc; + size_t offset; + int has_arg; + char short_opt; + unsigned long min; + unsigned long max; + print_function print; /* Overrides type->print. */ + validate_function validate; /* Overrides type->validate. */ + char *candidates; /* Overrides type->candidates. */ + bool required; +}; + +int handle_option(struct rpki_config *, struct option_field *, char *); +int handle_flags_config(int , char **, struct rpki_config *); + +void get_global_fields(struct option_field **, unsigned int *); + +void get_tal_fields(struct option_field **, unsigned int *); #endif /* SRC_CONFIG_H_ */ diff --git a/src/main.c b/src/main.c index f0f9f793..7f0965b6 100644 --- a/src/main.c +++ b/src/main.c @@ -9,6 +9,7 @@ #include "log.h" #include "rpp.h" #include "thread_var.h" +#include "toml_handler.h" #include "object/certificate.h" #include "object/manifest.h" #include "object/tal.h" @@ -116,60 +117,6 @@ handle_file_config(char *config_file, struct rpki_config *config) return set_config_from_file(config_file, config); } -static int -handle_flags_config(int argc, char **argv, struct rpki_config *config) -{ - int opt, error = 0; - - config->flag_config = true; - - static struct option long_options[] = { - {"tal", required_argument, NULL, 't'}, - {"local_repository", required_argument, NULL, 'l'}, - {"disable_rsync", no_argument, 0, 'r'}, - {"shuffle_uris", no_argument, 0, 's'}, - {0,0,0,} - }; - - while ((opt = getopt_long(argc, argv, "t:l:rs", long_options, NULL)) - != -1) { - switch (opt) { - case 't' : - config->tal = optarg; - break; - case 'l' : - config->local_repository = optarg; - break; - case 'r': - config->enable_rsync = false; - break; - case 's': - config->shuffle_uris = true; - break; - default: - return pr_err("some usage hints.");/* TODO */ - } - } - - if (config->tal == NULL) { - fprintf(stderr, "Missing flag --tal \n"); - error = -EINVAL; - } - if(config->local_repository == NULL) { - fprintf(stderr, "Missing flag --local_repository \n"); - error = -EINVAL; - } - - pr_debug("TAL file : %s", config->tal); - pr_debug("Local repository : %s", config->local_repository); - pr_debug("Enable rsync : %s", config->enable_rsync - ? "true" : "false"); - pr_debug("shuffle uris : %s", config->shuffle_uris - ? "true" : "false"); - - return error; -} - static int handle_args(int argc, char **argv, struct rpki_config *config) { diff --git a/src/toml_handler.c b/src/toml_handler.c new file mode 100644 index 00000000..9526d36b --- /dev/null +++ b/src/toml_handler.c @@ -0,0 +1,152 @@ +#include "toml_handler.h" + +#include +#include +#include +#include + +#include + +#include "file.h" +#include "log.h" +#include "thread_var.h" +#include "uri.h" + + +static void +print_config(struct rpki_config *config) +{ + pr_debug("Program configuration"); + pr_debug_add("{"); + pr_debug("%s: %s", "local_repository", config->local_repository); + pr_debug("%s: %s", "tal.file", config->tal); + pr_debug("%s: %s", "enable_rsync", + config->enable_rsync ? "true" : "false"); + pr_debug("%s: %s", "tal.shuffle_uris", + config->shuffle_uris ? "true" : "false"); + pr_debug_rm("}"); +} + +static int +iterate_fields(struct toml_table_t *table, struct rpki_config *config, + struct option_field *fields, unsigned int field_len) +{ + struct option_field *field; + const char *result; + char *str; + int i, error, missing_param; + + missing_param = 0; + + for (i = 0; i < field_len; i++) { + field = &(fields[i]); + result = toml_raw_in(table, field->name); + if (result == 0) { + if (field->required) { + printf("Required parameter is missing '%s'\n", + field->name); + missing_param |= -ENOENT; + } + continue; + } + + str = (char *) result; + if (field->type->id == GTI_STRING) { + error = toml_rtos(result, &str); + if (error) + return pr_err("Bad value in '%s'", + field->name); + } + + error = handle_option(config, field, str); + if (error) + return error; + + } + + if (missing_param) + return missing_param; + return error; +} + +static int +handle_tal_table(struct toml_table_t *tal, struct rpki_config *config) +{ + struct option_field *tal_fields; + unsigned int tal_len; + + get_tal_fields(&tal_fields, &tal_len); + + return iterate_fields(tal, config, tal_fields, tal_len); +} + +static int +toml_to_config(struct toml_table_t *root, struct rpki_config *config) +{ + struct option_field *globals; + struct toml_table_t *tal; + int error; + unsigned int global_len; + + + get_global_fields(&globals, &global_len); + error = iterate_fields(root, config, globals, global_len); + if (error) + return error; + + tal = toml_table_in(root, "tal"); + if (tal != 0) + error = handle_tal_table(tal, config); + else + return pr_err("Required table '%s' is missing.", "tal"); + + return error; +} + +int +set_config_from_file(char *config_file, struct rpki_config *config) +{ + struct file_contents fc; + struct toml_table_t *root; + struct rpki_uri uri; + char errbuf[200]; + int error; + bool is_config_file; + + /* I think I'm not using this struct correctly but I need it to call + * some functions, so be careful using the struct rpki_uri here. + * Also no needs to be freed. */ + uri.global = config_file; + uri.global_len = strlen(config_file); + uri.local = config_file; + + is_config_file = uri_has_extension(&uri, ".ini"); + is_config_file |= uri_has_extension(&uri, ".toml"); + if (!is_config_file) { + error = pr_err("Invalid Config file extension for file '%s'", + uri.local); + goto end; + } + + error = file_load(&uri, &fc); + if (error) + goto end; /* Error msg already printed. */ + + root = toml_parse((char *) fc.buffer, errbuf, sizeof(errbuf)); + file_free(&fc); + + if (root == NULL) { + error = pr_err("Error while parsing configuration file: %s", + errbuf); + goto end; + } + + error = toml_to_config(root, config); + + toml_free(root); + + print_config(config); + +end: + return error; +} diff --git a/src/toml_handler.h b/src/toml_handler.h new file mode 100644 index 00000000..b75f20f8 --- /dev/null +++ b/src/toml_handler.h @@ -0,0 +1,8 @@ +#ifndef SRC_TOML_HANDLER_H_ +#define SRC_TOML_HANDLER_H_ + +#include "config.h" + +int set_config_from_file(char *, struct rpki_config *); + +#endif /* SRC_TOML_HANDLER_H_ */