From: dhfelix Date: Thu, 14 Feb 2019 21:57:03 +0000 (-0600) Subject: Update args parser framework X-Git-Tag: v0.0.2~87^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0b1e303db8886f20f20cbb1788c4cc85cfa93d59;p=thirdparty%2FFORT-validator.git Update args parser framework --- diff --git a/src/config.c b/src/config.c index 6b25b719..92f31e2c 100644 --- a/src/config.c +++ b/src/config.c @@ -1,31 +1,44 @@ #include "config.h" #include -#include +#include #include #include #include "common.h" #include "log.h" +#include "toml_handler.h" #define OPT_FIELD_ARRAY_LEN(array) ARRAY_LEN(array) - 1 -struct args_flag { - struct option_field *field; - bool is_set; +struct rpki_config { + /* tal file path*/ + char *tal; + /* Local repository path */ + char *local_repository; + /* Enable rsync downloads */ + bool enable_rsync; + /* Shuffle uris in tal */ + bool shuffle_uris; + /* + * rfc6487#section-7.2, last paragraph. + * Prevents arbitrarily long paths and loops. + */ + unsigned int maximum_certificate_depth; }; static int parse_bool(struct option_field *, char *, void *); static int parse_u_int(struct option_field *, char *, void *); +static void _free_rpki_config(struct rpki_config *); -static struct rpki_config config; +static struct rpki_config rpki_config; static struct global_type gt_bool = { .id = GTI_BOOL, .name = "Boolean", .size = sizeof(bool), .parse = parse_bool, - .candidates = "true false", + .candidates = "true|false", }; static struct global_type gt_string = { @@ -38,9 +51,19 @@ static struct global_type gt_u_int = { .name = "Unsigned Int", .size = sizeof(unsigned int), .parse = parse_u_int, - .candidates = "Unsigned Int", + .candidates = "NUM", +}; + +static struct option manual_long_opts[] = { + { + .name = "configuration-file", + .has_arg = required_argument, + .flag = NULL, + .val = 'f', + }, }; + static struct option_field global_fields[] = { { .name = "local-repository", @@ -91,8 +114,8 @@ static struct option_field tal_fields[] = { .short_opt = 0, .min = 1, /** - * It cannot be UINT_MAX, because then the actual number will overflow - * and will never be bigger than this. + * It cannot be UINT_MAX, because then the actual number will + * overflow and will never be bigger than this. */ .max = UINT_MAX - 1, .required = false, @@ -100,6 +123,22 @@ static struct option_field tal_fields[] = { { NULL }, }; + + +static struct group_fields fields[] = { + { + .group_name = "root", + .options = global_fields, + .options_len = OPT_FIELD_ARRAY_LEN(global_fields), + }, + { + .group_name = "tal", + .options = tal_fields, + .options_len = OPT_FIELD_ARRAY_LEN(tal_fields), + }, + { NULL }, +}; + static int str_to_bool(const char *str, bool *bool_out) { @@ -129,26 +168,17 @@ parse_bool(struct option_field *field, char *str, void *result) *value = true; return 0; break; - case required_argument: case optional_argument: - if (field->has_arg == optional_argument && str == NULL) { + if (str == NULL) { *value = true; - return 0; + break; } - /** - * XXX: (fine) GETOPT should had ensure that the code did - * not reach here for this particular case. - * */ + /* FALLTHROUGH */ + case required_argument: return str_to_bool(str, result); - break; - } - - if (str == NULL) { - *value = true; - return 0; } - return str_to_bool(str, result); + return 0; } static int str_to_ull(const char *str, char **endptr, @@ -199,46 +229,59 @@ static int construct_options(struct args_flag **flags, struct option **long_options, int *flags_len) { + struct option_field *tmp; + struct group_fields *tmp_all_fields; 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; - - get_global_fields(&global, &global_len); - get_tal_fields(&tal, &tal_len); - - total_len = global_len + tal_len; + unsigned int total_len, i, result_idx, + extra_long_options; + + tmp_all_fields = fields; + total_len = 0; + while (tmp_all_fields->group_name != NULL) { + total_len += tmp_all_fields->options_len; + tmp_all_fields += 1; + } - result_flags = calloc(total_len, sizeof(struct args_flag)); + /* +1 NULL end, means end of array. */ + result_flags = calloc(total_len + 1, 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)); + extra_long_options = ARRAY_LEN(manual_long_opts); + + result_options = calloc(total_len + + extra_long_options /* extra options handled manually. */ + + 1, /* long options must end with zeros */ + sizeof(struct option)); if (result_options == NULL) { free(result_flags); return pr_enomem(); } result_idx = 0; - for(i = 0; i < global_len; i++) { - result_flags[result_idx].field = &(global[i]); - - 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++; + tmp_all_fields = fields; + while (tmp_all_fields->group_name != NULL) { + tmp = tmp_all_fields->options; + while(tmp->name != NULL) { + result_flags[result_idx].field = tmp; + + result_options[result_idx].name = tmp->name; + result_options[result_idx].has_arg = tmp->has_arg; + result_options[result_idx].val = tmp->short_opt; + result_options[result_idx].flag = NULL; + + result_idx++; + tmp += 1; + } + tmp_all_fields += 1; } - 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; + for (i = 0; i < extra_long_options; i++) { + result_options[result_idx].name = manual_long_opts[i].name; + result_options[result_idx].has_arg = manual_long_opts[i].has_arg; + result_options[result_idx].val = manual_long_opts[i].val; + result_options[result_idx].flag = manual_long_opts[i].flag; result_idx++; } @@ -250,10 +293,21 @@ construct_options(struct args_flag **flags, struct option **long_options, return 0; } -static void +static int set_string(void **field, char *str) { - *field = str; + char *result; + + /* malloc the string, because if the string comes from TOML_HANDLER, + * the string is freed later in that function. */ + result = malloc(strlen(str) + 1); + if (result == NULL) + return pr_enomem(); + + strcpy(result, str); + *field = result; + + return 0; } static void @@ -280,7 +334,9 @@ handle_option(struct rpki_config *config, struct option_field *field, char *str) config_param += field->offset; if (field->type == >_string) { - set_string(config_param, str); + error = set_string(config_param, str); + if (error) + return error; set_config_param_for_string(config_param, &config_param); } else if (field->type->parse != NULL){ error = field->type->parse(field, str, config_param); @@ -307,7 +363,7 @@ check_missing_flags(struct args_flag *flag) if (!flag->field->required) return 0; - printf("Missing flag --%s", flag->field->name); + printf("Missing param: %s", flag->field->name); switch (flag->field->has_arg) { case no_argument: break; @@ -352,44 +408,151 @@ print_config(struct rpki_config *config) pr_debug_rm("}"); } +static void +set_default_values(struct rpki_config *config) +{ + config->enable_rsync = true; + config->local_repository = NULL; + config->maximum_certificate_depth = 32; + config->shuffle_uris = false; + config->tal = NULL; +} + +static void _print_usage(bool only_required) +{ + struct option_field *tmp; + struct group_fields *tmp_all_fields; + char *candidates; + bool required; + + get_group_fields(&tmp_all_fields); + + while (tmp_all_fields->group_name != NULL) { + tmp = tmp_all_fields->options; + while(tmp->name != NULL) { + required = tmp->required; + + if (only_required != required) { + tmp += 1; + continue; + } + + fprintf(stderr, " "); + + if (!required) + fprintf(stderr, "["); + fprintf(stderr, "--%s", tmp->name); + + if (tmp->candidates != NULL) + candidates = tmp->candidates; + else if (tmp->type->candidates != NULL) + candidates = tmp->type->candidates; + else + candidates = NULL; + + switch (tmp->has_arg) { + case no_argument: + break; + case optional_argument: + if(candidates == NULL) + break; + fprintf(stderr, "=<%s>", candidates); + break; + case required_argument: + if(candidates == NULL) + break; + fprintf(stderr, " <%s>", candidates); + break; + default: + break; + } + if (!required) + fprintf(stderr, "]"); + tmp += 1; + } + tmp_all_fields += 1; + } + +} + + +void +print_usage(char *progname) +{ + /* + * TODO openbsd styleguide said use "getprogname" to set the progam + * name. + */ + fprintf(stderr, "usage: %s", progname); + + fprintf(stderr, " [-f ] " + "[--configuration-file ]"); + + _print_usage(true); + _print_usage(false); + + fprintf(stderr, "\n"); + exit(1); +} + int -handle_flags_config(int argc, char **argv, struct rpki_config *config) +handle_flags_config(int argc, char **argv) { struct args_flag *flags; struct option *long_options; - int opt, indexptr, flags_len, error = 0; + struct rpki_config config; + int opt, indexptr, flags_len, error; + + set_default_values(&config); - flags = NULL; long_options = NULL; + flags = NULL; + flags_len = 0; error = construct_options(&flags, &long_options, &flags_len); if (error) return error; /* Error msg already printed. */ - while ((opt = getopt_long(argc, argv, "", long_options, &indexptr)) - != -1) { + while ((opt = getopt_long(argc, argv, "f:", long_options, &indexptr)) + != -1) switch (opt) { case 0: flags[indexptr].is_set = true; - error = handle_option(config, flags[indexptr].field, + error = handle_option(&config, flags[indexptr].field, optarg); + if (error) { + print_usage(argv[0]); + goto end; + } break; - default: - error = pr_err("some usage hints.");/* TODO */ + case 'f': + error = set_config_from_file(optarg, &config, flags); + if (error) { + print_usage(argv[0]); + goto end; + } break; - } - - if (error) + default: + print_usage(argv[0]); + error = -EINVAL; goto end; - } + } - for (indexptr = 0; indexptr < flags_len; indexptr++) { + for (indexptr = 0; indexptr < flags_len; indexptr++) error |= check_missing_flags(&flags[indexptr]); + + if (error) { + print_usage(argv[0]); + goto end; } - print_config(config); + print_config(&config); + config_set(&config); end: + if (error) + _free_rpki_config(&config); + free(flags); free(long_options); return error; @@ -397,55 +560,60 @@ end: } 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) +get_group_fields(struct group_fields **group_fields) { - if (fields) - *fields = tal_fields; - if (len) - *len = OPT_FIELD_ARRAY_LEN(tal_fields); + if (group_fields) + *group_fields = fields; } void config_set(struct rpki_config *new) { - config = *new; + rpki_config = *new; } char const * config_get_tal(void) { - return config.tal; + return rpki_config.tal; } char const * config_get_local_repository(void) { - return config.local_repository; + return rpki_config.local_repository; } bool config_get_enable_rsync(void) { - return config.enable_rsync; + return rpki_config.enable_rsync; } bool config_get_shuffle_uris(void) { - return config.shuffle_uris; + return rpki_config.shuffle_uris; } unsigned int config_get_max_cert_depth(void) { - return config.maximum_certificate_depth; + return rpki_config.maximum_certificate_depth; +} + +static void +_free_rpki_config(struct rpki_config *config) +{ + if (config->local_repository != NULL) + free(config->local_repository); + + if (config->tal != NULL) + free(config->tal); +} + +void +free_rpki_config(void) +{ + _free_rpki_config(&rpki_config); } diff --git a/src/config.h b/src/config.h index 54a64e5a..14fac274 100644 --- a/src/config.h +++ b/src/config.h @@ -4,21 +4,9 @@ #include #include -struct rpki_config { - /* tal file path*/ - char *tal; - /* Local repository path */ - char *local_repository; - /* Enable rsync downloads */ - bool enable_rsync; - /* Shuffle uris in tal */ - bool shuffle_uris; - /* - * rfc6487#section-7.2, last paragraph. - * Prevents arbitrarily long paths and loops. - */ - unsigned int maximum_certificate_depth; -}; +struct rpki_config; + +struct option_field; typedef enum global_type_id { GTI_BOOL, @@ -26,13 +14,16 @@ typedef enum global_type_id { GTI_U_INT, } 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 args_flag { + struct option_field *field; + bool is_set; +}; + struct global_type { global_type_id id; const char *name; @@ -58,12 +49,17 @@ struct option_field { bool required; }; -int handle_option(struct rpki_config *, struct option_field *, char *); -int handle_flags_config(int , char **, struct rpki_config *); +struct group_fields { + char *group_name; + struct option_field *options; + unsigned int options_len; +}; -void get_global_fields(struct option_field **, unsigned int *); +void print_usage(char *progname); +int handle_option(struct rpki_config *, struct option_field *, char *); +int handle_flags_config(int , char **); -void get_tal_fields(struct option_field **, unsigned int *); +void get_group_fields(struct group_fields **); void config_set(struct rpki_config *); @@ -72,5 +68,6 @@ char const *config_get_local_repository(void); bool config_get_enable_rsync(void); bool config_get_shuffle_uris(void); unsigned int config_get_max_cert_depth(void); +void free_rpki_config(void); #endif /* SRC_CONFIG_H_ */ diff --git a/src/main.c b/src/main.c index ae826fbd..4b15ffb5 100644 --- a/src/main.c +++ b/src/main.c @@ -9,7 +9,6 @@ #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" @@ -112,62 +111,21 @@ end: return error; } -static int -handle_file_config(char *config_file, struct rpki_config *config) -{ - return set_config_from_file(config_file, config); -} - -static int -handle_args(int argc, char **argv) -{ - struct rpki_config config; - char *config_file; - int error; - - config.enable_rsync = true; - config.local_repository = NULL; - config.maximum_certificate_depth = 32; - config.shuffle_uris = false; - config.tal = NULL; - - if (argc == 1) { - return pr_err("Show usage"); /*TODO*/ - } - if (strcasecmp(argv[1], "--configuration_file") == 0) { - if (argc == 2) { - return pr_err("--configuration_file requires a string " - "as argument."); - } - config_file = argv[2]; - argc -= 2; - argv += 2; - error = handle_file_config(config_file, &config); - } else { - error = handle_flags_config(argc, argv, &config); - } - - if (!error) - config_set(&config); - - return error; -} - - int main(int argc, char **argv) { struct tal *tal; int error; - error = handle_args(argc, argv); + error = handle_flags_config(argc, argv); if (error) return error; + print_stack_trace_on_segfault(); - error = rsync_init(); + error = rsync_init(config_get_enable_rsync()); if (error) - return error; + goto end; add_rpki_oids(); thvar_init(); @@ -184,5 +142,7 @@ main(int argc, char **argv) } rsync_destroy(); +end: + free_rpki_config(); return error; } diff --git a/src/rsync/rsync.c b/src/rsync/rsync.c index 2072d4e9..7c435d40 100644 --- a/src/rsync/rsync.c +++ b/src/rsync/rsync.c @@ -23,14 +23,16 @@ SLIST_HEAD(uri_list, uri); static struct uri_list *rsync_uris; static char const *const RSYNC_PREFIX = "rsync://"; +static bool rsync_enabled; //static const char *rsync_command[] = {"rsync", "--recursive", "--delete", "--times", NULL}; int -rsync_init(void) +rsync_init(bool is_rsync_enable) { /* Disabling rsync will forever be a useful debugging feature. */ - if (!config_get_enable_rsync()) + rsync_enabled = is_rsync_enable; + if (!rsync_enabled) return 0; rsync_uris = malloc(sizeof(struct uri_list)); @@ -46,7 +48,7 @@ rsync_destroy(void) { struct uri *uri; - if (!config_get_enable_rsync()) + if (!rsync_enabled) return; while (!SLIST_EMPTY(rsync_uris)) { @@ -378,7 +380,7 @@ download_files(struct rpki_uri const *uri) prefix_len = strlen(RSYNC_PREFIX); - if (!config_get_enable_rsync()) + if (!rsync_enabled) return 0; if (uri->global_len < prefix_len || diff --git a/src/rsync/rsync.h b/src/rsync/rsync.h index f4114df6..688c1df6 100644 --- a/src/rsync/rsync.h +++ b/src/rsync/rsync.h @@ -5,7 +5,7 @@ #include "uri.h" int download_files(struct rpki_uri const *); -int rsync_init(void); +int rsync_init(bool); void rsync_destroy(void); diff --git a/src/toml_handler.c b/src/toml_handler.c index 58bd754e..1f793104 100644 --- a/src/toml_handler.c +++ b/src/toml_handler.c @@ -12,103 +12,117 @@ #include "thread_var.h" #include "uri.h" - -static void -print_config(struct rpki_config *config) +static int +find_flag(struct args_flag *flags_handled, char *flag_to_find, + struct args_flag **result) { - 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("%s: %u", "tal.maximum-certificate-depth", - config->maximum_certificate_depth); - pr_debug_rm("}"); + int cmp; + *result = NULL; + + while(flags_handled->field != NULL) { + cmp = strcmp(flags_handled->field->name, flag_to_find); + if (cmp == 0) { + *result = flags_handled; + break; + } + flags_handled = flags_handled + 1; + } + + if (*result == NULL) + return pr_crit("Missing parameter %s.", flag_to_find); + + return 0; } static int iterate_fields(struct toml_table_t *table, struct rpki_config *config, - struct option_field *fields, unsigned int field_len) + struct option_field *fields, struct args_flag *flags_handled) { - struct option_field *field; + struct option_field *tmp_field; + struct args_flag *tmp_arg; const char *result; char *str; - int i, error, missing_param; + int error; - missing_param = 0; + tmp_field = fields; + while (tmp_field->name != NULL) { + error = find_flag(flags_handled, tmp_field->name, &tmp_arg); + if (error) + return error; /* Error msg already printed. */ + if (tmp_arg->is_set) { + tmp_field += 1; + continue; + } - for (i = 0; i < field_len; i++) { - field = &(fields[i]); - result = toml_raw_in(table, field->name); + result = toml_raw_in(table, tmp_field->name); if (result == 0) { - if (field->required) { - printf("Required parameter is missing '%s'\n", - field->name); - missing_param |= -ENOENT; - } + tmp_field += 1; continue; } str = (char *) result; - if (field->type->id == GTI_STRING) { + if (tmp_field->type->id == GTI_STRING) { error = toml_rtos(result, &str); if (error) return pr_err("Bad value in '%s'", - field->name); + tmp_field->name); } - error = handle_option(config, field, str); + error = handle_option(config, tmp_field, str); if (error) return error; + /* Free returned string from toml */ + if (tmp_field->type->id == GTI_STRING) + free(str); + + tmp_arg->is_set = true; + tmp_field += 1; } - if (missing_param) - return missing_param; return error; } static int -handle_tal_table(struct toml_table_t *tal, struct rpki_config *config) +toml_to_config(struct toml_table_t *root, struct rpki_config *config, + struct args_flag *flags_handled) { - 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; + struct toml_table_t *toml_table; + struct group_fields *group_fields; int error; - unsigned int global_len; + get_group_fields(&group_fields); + if (group_fields == NULL) + return 0; - get_global_fields(&globals, &global_len); - error = iterate_fields(root, config, globals, global_len); + error = iterate_fields(root, config, group_fields->options, + flags_handled); if (error) return error; + group_fields += 1; - 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"); + while (group_fields->group_name != NULL) { + toml_table = toml_table_in(root, group_fields->group_name); + if (toml_table == 0) { + group_fields += 1; + continue; + } + error = iterate_fields(toml_table, config, + group_fields->options, flags_handled); + if (error) + return error; + group_fields += 1; + } return error; } int -set_config_from_file(char *config_file, struct rpki_config *config) +set_config_from_file(char *config_file, struct rpki_config *config, + struct args_flag *flags_handled) { - struct file_contents fc; + FILE *file; + struct stat stat; struct toml_table_t *root; struct rpki_uri uri; char errbuf[200]; @@ -130,12 +144,12 @@ set_config_from_file(char *config_file, struct rpki_config *config) goto end; } - error = file_load(&uri, &fc); + error = file_open(&uri, &file, &stat); if (error) goto end; /* Error msg already printed. */ - root = toml_parse((char *) fc.buffer, errbuf, sizeof(errbuf)); - file_free(&fc); + root = toml_parse_file(file, errbuf, sizeof(errbuf)); + file_close(file); if (root == NULL) { error = pr_err("Error while parsing configuration file: %s", @@ -143,12 +157,10 @@ set_config_from_file(char *config_file, struct rpki_config *config) goto end; } - error = toml_to_config(root, config); + error = toml_to_config(root, config, flags_handled); toml_free(root); - print_config(config); - end: return error; } diff --git a/src/toml_handler.h b/src/toml_handler.h index b75f20f8..ae04120a 100644 --- a/src/toml_handler.h +++ b/src/toml_handler.h @@ -3,6 +3,6 @@ #include "config.h" -int set_config_from_file(char *, struct rpki_config *); +int set_config_from_file(char *, struct rpki_config *, struct args_flag *); #endif /* SRC_TOML_HANDLER_H_ */ diff --git a/test/Makefile.am b/test/Makefile.am index d3199efd..b4c72bff 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -23,6 +23,8 @@ line_file_test_SOURCES += line_file_test.c ../src/line_file.c ../src/line_file.h line_file_test_LDADD = ${MY_LDADD} tal_test_SOURCES = ../src/file.c ../src/file.h ../src/log.c ../src/log.h +tal_test_SOURCES += ../src/config.c ../src/config.h +tal_test_SOURCES += ../src/toml_handler.c ../src/toml_handler.h tal_test_SOURCES += ../src/common.c ../src/common.h tal_test_SOURCES += ../src/crypto/base64.c ../src/crypto/base64.h tal_test_SOURCES += ../src/random.c ../src/random.h @@ -32,6 +34,8 @@ tal_test_CFLAGS = ${AM_CFLAGS} ${GLIB_CFLAGS} tal_test_LDADD = ${MY_LDADD} ${GLIB_LIBS} rsync_test_SOURCES = ../src/file.c ../src/file.h ../src/log.c ../src/log.h +rsync_test_SOURCES += ../src/config.c ../src/config.h +rsync_test_SOURCES += ../src/toml_handler.c ../src/toml_handler.h rsync_test_SOURCES += ../src/uri.c ../src/uri.h rsync_test_SOURCES += ../src/common.c ../src/common.h rsync_test_SOURCES += rsync_test.c