]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Configuration framework review
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Mon, 18 Feb 2019 18:09:49 +0000 (12:09 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Mon, 18 Feb 2019 18:21:54 +0000 (12:21 -0600)
- Integrate the string data type to the configuration framework properly
  (They are no longer being handled as an exception)
- Integrate the print and free operations to the the framework
  (The rpki_config object is no longer printed and freed manually)
- Add short option support (eg. `-t` instead of `--tal`)
- Add --help, --version and --usage handlers
- Add a bunch of in-code documentation to most configuration fields

src/config.c
src/config.h
src/log.c
src/log.h
src/main.c
src/nid.c
src/object/tal.c
src/toml_handler.c
src/toml_handler.h

index 92f31e2c7ead0f0b5b0716c5edd9f5a53a4f684d..5b59a29c06d744241ce04fff011d2768b30fb632 100644 (file)
@@ -1,23 +1,28 @@
 #include "config.h"
 
+#include <limits.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <getopt.h>
 
-#include "common.h"
 #include "log.h"
 #include "toml_handler.h"
 
-#define OPT_FIELD_ARRAY_LEN(array) ARRAY_LEN(array) - 1
+#define FOREACH_OPTION(groups, grp, opt, type)                 \
+       for (grp = groups; grp->name != NULL; grp++)            \
+               for (opt = grp->options; opt->id != 0; opt++)   \
+                       if ((opt->availability == 0) ||         \
+                           (opt->availability & type))
 
 struct rpki_config {
        /* tal file path*/
-       char *tal;
+       char const *tal;
        /* Local repository path */
-       char *local_repository;
-       /* Enable rsync downloads */
-       bool enable_rsync;
+       char const *local_repository;
+       /* Disable rsync downloads */
+       bool disable_rsync;
        /* Shuffle uris in tal */
        bool shuffle_uris;
        /*
@@ -27,549 +32,509 @@ struct rpki_config {
        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 void print_usage(FILE *stream);
+
+void print_bool(struct group_fields const *, struct option_field const *,
+    void *);
+void print_u_int(struct group_fields const *, struct option_field const *,
+    void *);
+void print_string(struct group_fields const *, struct option_field const *,
+    void *);
+static int parse_bool(struct option_field const *, char const *, void *);
+static int parse_u_int(struct option_field const *, char const *, void *);
+static int parse_string(struct option_field const *, char const *, void *);
+static int handle_help(struct option_field const *, char *);
+static int handle_usage(struct option_field const *, char *);
+static int handle_version(struct option_field const *, char *);
+static int handle_toml(struct option_field const *, char *);
+static void free_string(void *);
+
+static char const *program_name;
 static struct rpki_config rpki_config;
 
-static struct global_type gt_bool = {
-       .id = GTI_BOOL,
-       .name = "Boolean",
+static const struct global_type gt_bool = {
+       .has_arg = no_argument,
        .size = sizeof(bool),
+       .print = print_bool,
        .parse = parse_bool,
-       .candidates = "true|false",
+       .arg_doc = "true|false",
 };
 
-static struct global_type gt_string = {
-       .id = GTI_STRING,
-       .name = "String",
-};
-
-static struct global_type gt_u_int = {
-       .id = GTI_U_INT,
-       .name = "Unsigned Int",
+static const struct global_type gt_u_int = {
+       .has_arg = required_argument,
        .size = sizeof(unsigned int),
+       .print = print_u_int,
        .parse = parse_u_int,
-       .candidates = "NUM",
+       .arg_doc = "<unsigned integer>",
 };
 
-static struct option manual_long_opts[] = {
-       {
-               .name = "configuration-file",
-               .has_arg = required_argument,
-               .flag = NULL,
-               .val = 'f',
-       },
+static const struct global_type gt_string = {
+       .has_arg = required_argument,
+       .size = sizeof(char *),
+       .print = print_string,
+       .parse = parse_string,
+       .free = free_string,
+       .arg_doc = "<string>",
 };
 
+/**
+ * An option that takes no arguments, is not correlated to any rpki_config
+ * fields, and is entirely managed by its handler function.
+ */
+static const struct global_type gt_callback = {
+       .has_arg = no_argument,
+};
 
-static struct option_field global_fields[] = {
+static const struct option_field global_fields[] = {
        {
+               .id = 'h',
+               .name = "help",
+               .type = &gt_callback,
+               .handler = handle_help,
+               .doc = "Give this help list",
+               .availability = AVAILABILITY_GETOPT,
+       }, {
+               .id = 1000,
+               .name = "usage",
+               .type = &gt_callback,
+               .handler = handle_usage,
+               .doc = "Give a short usage message",
+               .availability = AVAILABILITY_GETOPT,
+       }, {
+               .id = 'V',
+               .name = "version",
+               .type = &gt_callback,
+               .handler = handle_version,
+               .doc = "Print program version",
+               .availability = AVAILABILITY_GETOPT,
+       }, {
+               .id = 'f',
+               .name = "configuration-file",
+               .type = &gt_string,
+               .handler = handle_toml,
+               .doc = "TOML file the configuration will be read from.",
+               .arg_doc = "<file>",
+               .availability = AVAILABILITY_GETOPT,
+       }, {
+               .id = 'r',
                .name = "local-repository",
                .type = &gt_string,
-               .doc = "Local repository path.",
                .offset = offsetof(struct rpki_config, local_repository),
-               .has_arg = required_argument,
-               .short_opt = 0,
-               .required = true,
-               .candidates = "path",
+               .doc = "Local repository path.",
+               .arg_doc = "<directory>",
        }, {
-               .name = "enable-rsync",
+               .id = 1001,
+               .name = "disable-rsync",
                .type = &gt_bool,
+               .offset = offsetof(struct rpki_config, disable_rsync),
                .doc = "Enable or disable rsync downloads.",
-               .offset = offsetof(struct rpki_config, enable_rsync),
-               .has_arg = optional_argument,
-               .short_opt = 0,
-               .required = false,
        },
-       { NULL },
+       { 0 },
 };
 
-static struct option_field tal_fields[] = {
+static const struct option_field tal_fields[] = {
        {
+               .id = 't',
                .name = "tal",
                .type = &gt_string,
-               .doc = "TAL file path",
                .offset = offsetof(struct rpki_config, tal),
-               .has_arg = required_argument,
-               .short_opt = 0,
-               .required = true,
-               .candidates = "file",
+               .doc = "TAL file path",
+               .arg_doc = "<file name>",
        }, {
+               .id = 2000,
                .name = "shuffle-uris",
                .type = &gt_bool,
-               .doc = "Shuffle URIs in the TAL.",
                .offset = offsetof(struct rpki_config, shuffle_uris),
-               .has_arg = optional_argument,
-               .short_opt = 0,
-               .required = false,
+               .doc = "Shuffle URIs in the TAL.",
        }, {
+               .id = 2001,
                .name = "maximum-certificate-depth",
                .type = &gt_u_int,
-               .doc = "Prevents arbitrarily long paths and loops.",
                .offset = offsetof(struct rpki_config,
                    maximum_certificate_depth),
-               .has_arg = required_argument,
-               .short_opt = 0,
+               .doc = "Prevents arbitrarily long paths and loops.",
                .min = 1,
                /**
                 * 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,
+               .max = UINT_MAX - 1,
        },
-       { NULL },
+       { 0 },
 };
 
-
-
-static struct group_fields fields[] = {
+static const struct group_fields groups[] = {
        {
-               .group_name = "root",
+               .name = "root",
                .options = global_fields,
-               .options_len = OPT_FIELD_ARRAY_LEN(global_fields),
-       },
-       {
-               .group_name = "tal",
+       }, {
+               .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)
+/**
+ * Returns true if @field is the descriptor of one of the members of the
+ * struct rpki_config structure, false otherwise.
+ * (Alternatively: Returns true if @field->offset is valid, false otherwise.)
+ */
+static bool
+is_rpki_config_field(struct option_field const *field)
 {
-       if (strcasecmp(str, "true") == 0 || strcasecmp(str, "1") == 0 ||
-           strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0) {
-               *bool_out = true;
-               return 0;
-       }
+       return field->handler == NULL;
+}
 
-       if (strcasecmp(str, "false") == 0 || strcasecmp(str, "0") == 0 ||
-           strcasecmp(str, "no") == 0 || strcasecmp(str, "off") == 0) {
-               *bool_out = false;
-               return 0;
-       }
+static void *
+get_rpki_config_field(struct option_field const *field)
+{
+       return ((unsigned char *) &rpki_config) + field->offset;
+}
+
+void
+print_bool(struct group_fields const *group, struct option_field const *field,
+    void *_value)
+{
+       bool *value = _value;
+       pr_info("%s.%s: %s", group->name, field->name,
+           (*value) ? "true" : "false");
+}
+
+void
+print_u_int(struct group_fields const *group, struct option_field const *field,
+    void *value)
+{
+       pr_info("%s.%s: %u", group->name, field->name,
+           *((unsigned int *) value));
+}
 
-       return pr_err("Cannot parse '%s' as a bool "
-                       "(true|false|1|0|yes|no|on|off).", str);
+void
+print_string(struct group_fields const *group, struct option_field const *field,
+    void *value)
+{
+       pr_info("%s.%s: %s", group->name, field->name, *((char **) value));
 }
 
 static int
-parse_bool(struct option_field *field, char *str, void *result)
+parse_bool(struct option_field const *field, char const *str, void *result)
 {
        bool *value = result;
 
-       switch (field->has_arg) {
-       case no_argument:
+       if (str == NULL) {
                *value = true;
                return 0;
-               break;
-       case optional_argument:
-               if (str == NULL) {
-                       *value = true;
-                       break;
-               }
-               /* FALLTHROUGH */
-       case required_argument:
-               return str_to_bool(str, result);
        }
 
-       return 0;
+       if (strcmp(str, "true") == 0) {
+               *value = true;
+               return 0;
+       }
+
+       if (strcmp(str, "false") == 0) {
+               *value = false;
+               return 0;
+       }
+
+       return pr_err("Cannot parse '%s' as a bool (true|false).", str);
 }
 
-static int str_to_ull(const char *str, char **endptr,
-    const unsigned long long int min,
-    const unsigned long long int max,
-    unsigned long long int *result)
+static int
+parse_u_int(struct option_field const *field, char const *str, void *_result)
 {
-       unsigned long long int parsed;
+       unsigned long parsed;
+       int *result;
+
+       if (field->type->has_arg != required_argument || str == NULL) {
+               return pr_err("Integer options ('%s' in this case) require an argument.",
+                   field->name);
+       }
 
        errno = 0;
-       parsed = strtoull(str, endptr, 10);
+       parsed = strtoul(str, NULL, 10);
        if (errno)
                return pr_errno(errno, "'%s' is not an unsigned integer", str);
 
-       if (parsed < min || max < parsed)
-               return pr_err("'%s' is out of bounds (%llu-%llu).", str, min,
-                   max);
+       if (parsed < field->min || field->max < parsed) {
+               return pr_err("'%lu' is out of bounds (%u-%u).", parsed,
+                   field->min, field->max);
+       }
 
+       result = _result;
        *result = parsed;
        return 0;
 }
 
 static int
-str_to_unsigned_int(const char *str, unsigned int *out, unsigned int min,
-    unsigned int max)
+parse_string(struct option_field const *field, char const *str, void *_result)
 {
-       unsigned long long int result = 0;
-       int error;
+       char **result = _result;
 
-       error = str_to_ull(str, NULL, min, max, &result);
+       if (field->type->has_arg != required_argument || str == NULL) {
+               return pr_err("String options ('%s' in this case) require an argument.",
+                   field->name);
+       }
 
-       *out = result;
-       return error;
+       /* tomlc99 frees @str early, so work with a copy. */
+       *result = strdup(str);
+       return ((*result) != NULL) ? 0 : pr_enomem();
 }
 
 static int
-parse_u_int(struct option_field *field, char *str, void *result)
+handle_help(struct option_field const *field, char *arg)
 {
-       unsigned int *value = result;
-
-       if (str == NULL)
-               return pr_err("String cannot be NULL");
-
-       return str_to_unsigned_int(str, value, field->min, field->max);
+       print_usage(stdout);
+       exit(0);
 }
 
 static int
-construct_options(struct args_flag **flags, struct option **long_options,
-    int *flags_len)
+handle_usage(struct option_field const *field, char *arg)
 {
-       struct option_field *tmp;
-       struct group_fields *tmp_all_fields;
-       struct args_flag *result_flags;
-       struct option *result_options;
-       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;
-       }
-
-       /* +1 NULL end, means end of array. */
-       result_flags = calloc(total_len + 1, sizeof(struct args_flag));
-       if (result_flags == NULL)
-               return pr_enomem();
-
-       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;
-       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 < 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++;
-       }
-
-       *flags = result_flags;
-       *flags_len = total_len;
-       *long_options = result_options;
-
-       return 0;
+       print_usage(stdout);
+       exit(0);
 }
 
 static int
-set_string(void **field, char *str)
+handle_version(struct option_field const *field, char *arg)
 {
-       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;
+       printf("0.0.1\n");
+       exit(0);
+}
 
-       return 0;
+static int
+handle_toml(struct option_field const *field, char *file_name)
+{
+       return set_config_from_file(file_name);
 }
 
 static void
-set_config_param_for_string(void **field, void **config_param)
+free_string(void *_string)
 {
-       *config_param = *field;
+       char **string = _string;
+       free(*string);
 }
 
-int
-handle_option(struct rpki_config *config, struct option_field *field, char *str)
+static bool
+is_alphanumeric(int chara)
 {
-       void *config_param;
-       int error = 0;
-
-       /**
-        * 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;
+       return ('a' <= chara && chara <= 'z')
+           || ('A' <= chara && chara <= 'Z')
+           || ('0' <= chara && chara <= '9');
+}
 
-       if (field->type == &gt_string) {
-               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);
+/**
+ * "struct option" is the array that getopt expects.
+ * "struct args_flag" is our option metadata.
+ */
+static int
+construct_getopt_options(struct option **_long_opts, char **_short_opts)
+{
+       struct group_fields const *group;
+       struct option_field const *opt;
+       struct option *long_opts;
+       char *short_opts;
+       unsigned int total_long_options;
+       unsigned int total_short_options;
+
+       total_long_options = 0;
+       total_short_options = 0;
+       FOREACH_OPTION(groups, group, opt, AVAILABILITY_GETOPT) {
+               total_long_options++;
+               if (is_alphanumeric(opt->id)) {
+                       total_short_options++;
+                       if (opt->type->has_arg != no_argument)
+                               total_short_options++; /* ":" */
+               }
        }
 
-       if (error)
-               return error;
+       /* +1 NULL end, means end of array. */
+       long_opts = calloc(total_long_options + 1, sizeof(struct option));
+       if (long_opts == NULL)
+               return pr_enomem();
+       short_opts = malloc(total_short_options + 1);
+       if (short_opts == NULL) {
+               free(long_opts);
+               return pr_enomem();
+       }
 
-       if (field->validate != NULL)
-               error = field->validate(field, config_param);
-       else if (field->type->validate != NULL)
-               error = field->type->validate(field, config_param);
+       *_long_opts = long_opts;
+       *_short_opts = short_opts;
+
+       FOREACH_OPTION(groups, group, opt, AVAILABILITY_GETOPT) {
+               long_opts->name = opt->name;
+               long_opts->has_arg = opt->type->has_arg;
+               long_opts->flag = NULL;
+               long_opts->val = opt->id;
+               long_opts++;
+
+               if (is_alphanumeric(opt->id)) {
+                       *short_opts = opt->id;
+                       short_opts++;
+                       if (opt->type->has_arg != no_argument) {
+                               *short_opts = ':';
+                               short_opts++;
+                       }
+               }
+       }
 
-       return error;
+       *short_opts = '\0';
+       return 0;
 }
 
 int
-check_missing_flags(struct args_flag *flag)
+parse_option(struct option_field const *field, char const *str)
 {
-       char *candidate = NULL;
+       return field->type->parse(field, str,
+           get_rpki_config_field(field));
+}
 
-       if (flag->is_set)
-               return 0;
-       if (!flag->field->required)
-               return 0;
+static void
+print_config(void)
+{
+       struct group_fields const *grp;
+       struct option_field const *opt;
 
-       printf("Missing param: %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;
-       }
+       pr_info("Configuration {");
+       pr_indent_add();
 
-       printf("\n");
+       FOREACH_OPTION(groups, grp, opt, 0xFFFF)
+               if (is_rpki_config_field(opt) && opt->type->print != NULL)
+                       opt->type->print(grp, opt, get_rpki_config_field(opt));
 
-       return -ENOENT;
+       pr_indent_rm();
+       pr_info("}");
 }
 
-static void
-print_config(struct rpki_config *config)
+static int
+set_default_values(void)
 {
-       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("%s: %u", "tal.maximum-certificate-depth",
-                   config->maximum_certificate_depth);
-       pr_debug_rm("}");
+       rpki_config.tal = NULL;
+       rpki_config.local_repository = strdup("repository/");
+       if (rpki_config.local_repository == NULL)
+               return pr_enomem();
+       rpki_config.disable_rsync = false;
+       rpki_config.shuffle_uris = false;
+       rpki_config.maximum_certificate_depth = 32;
+       return 0;
 }
 
-static void
-set_default_values(struct rpki_config *config)
+static int
+validate_config(void)
 {
-       config->enable_rsync = true;
-       config->local_repository = NULL;
-       config->maximum_certificate_depth = 32;
-       config->shuffle_uris = false;
-       config->tal = NULL;
+       return (rpki_config.tal != NULL)
+           ? 0
+           : pr_err("The TAL file (--tal) is mandatory.");
 }
 
-static void _print_usage(bool only_required)
+static void
+print_usage(FILE *stream)
 {
-       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;
-                       }
+       struct group_fields const *group;
+       struct option_field const *option;
+       char const *arg_doc;
 
-                       fprintf(stderr, " ");
+       fprintf(stream, "Usage: %s\n", program_name);
 
-                       if (!required)
-                               fprintf(stderr, "[");
-                       fprintf(stderr, "--%s", tmp->name);
+       FOREACH_OPTION(groups, group, option, AVAILABILITY_GETOPT) {
+               fprintf(stream, "\t[");
+               fprintf(stream, "--%s", option->name);
 
-                       if (tmp->candidates != NULL)
-                               candidates = tmp->candidates;
-                       else if (tmp->type->candidates != NULL)
-                               candidates = tmp->type->candidates;
-                       else
-                               candidates = NULL;
+               if (option->arg_doc != NULL)
+                       arg_doc = option->arg_doc;
+               else if (option->type->arg_doc != NULL)
+                       arg_doc = option->type->arg_doc;
+               else
+                       arg_doc = 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:
+               switch (option->type->has_arg) {
+               case no_argument:
+                       break;
+               case optional_argument:
+               case required_argument:
+                       if(arg_doc == NULL)
                                break;
-                       }
-                       if (!required)
-                               fprintf(stderr, "]");
-                       tmp += 1;
+                       fprintf(stream, "=%s", arg_doc);
+                       break;
                }
-               tmp_all_fields += 1;
-       }
 
+               fprintf(stream, "]\n");
+       }
 }
 
 
-void
-print_usage(char *progname)
+static int
+handle_opt(int opt)
 {
-       /*
-        * TODO openbsd styleguide said use "getprogname" to set the progam
-        * name.
-        */
-       fprintf(stderr, "usage: %s", progname);
-
-       fprintf(stderr, " [-f <config_file>] "
-           "[--configuration-file <config_file>]");
-
-       _print_usage(true);
-       _print_usage(false);
+       struct group_fields const *group;
+       struct option_field const *option;
+
+       FOREACH_OPTION(groups, group, option, AVAILABILITY_GETOPT) {
+               if (option->id == opt) {
+                       return is_rpki_config_field(option)
+                           ? parse_option(option, optarg)
+                           : option->handler(option, optarg);
+               }
+       }
 
-       fprintf(stderr, "\n");
-       exit(1);
+       pr_err("Unrecognized option: %d", opt);
+       return -ESRCH;
 }
 
 int
 handle_flags_config(int argc, char **argv)
 {
-       struct args_flag *flags;
-       struct option *long_options;
-       struct rpki_config config;
-       int opt, indexptr, flags_len, error;
-
-       set_default_values(&config);
+       struct option *long_opts;
+       char *short_opts;
+       int opt;
+       int error;
 
-       long_options = NULL;
-       flags = NULL;
-       flags_len = 0;
+       program_name = argv[0];
+       error = set_default_values();
+       if (error)
+               return error;
 
-       error = construct_options(&flags, &long_options, &flags_len);
+       long_opts = NULL;
+       short_opts = NULL;
+       error = construct_getopt_options(&long_opts, &short_opts);
        if (error)
-               return error; /* Error msg already printed. */
-
-       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,
-                           optarg);
-                       if (error) {
-                               print_usage(argv[0]);
-                               goto end;
-                       }
-                       break;
-               case 'f':
-                       error = set_config_from_file(optarg, &config, flags);
-                       if (error) {
-                               print_usage(argv[0]);
-                               goto end;
-                       }
-                       break;
-               default:
-                       print_usage(argv[0]);
-                       error = -EINVAL;
-                       goto end;
-               }
+               goto end; /* Error msg already printed. */
 
-       for (indexptr = 0; indexptr < flags_len; indexptr++)
-               error |= check_missing_flags(&flags[indexptr]);
+       while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL))
+           != -1) {
+               error = handle_opt(opt);
+               if (error)
+                       goto end;
+       }
 
-       if (error) {
-               print_usage(argv[0]);
+       /*
+        * This triggers when the user runs something like
+        * `rpki-validator disable-rsync` instead of
+        * `rpki-validator --disable-rsync`.
+        * This program does not have unflagged payload.
+        */
+       if (optind < argc) {
+               error = pr_err("I don't know what '%s' is.", argv[optind]);
                goto end;
        }
 
-       print_config(&config);
-       config_set(&config);
+       error = validate_config();
 
 end:
        if (error)
-               _free_rpki_config(&config);
+               free_rpki_config();
+       else
+               print_config();
 
-       free(flags);
-       free(long_options);
+       free(long_opts);
+       free(short_opts);
        return error;
 
 }
 
 void
-get_group_fields(struct group_fields **group_fields)
+get_group_fields(struct group_fields const **group_fields)
 {
-       if (group_fields)
-               *group_fields = fields;
-}
-
-void
-config_set(struct rpki_config *new)
-{
-       rpki_config = *new;
+       *group_fields = groups;
 }
 
 char const *
@@ -587,7 +552,7 @@ config_get_local_repository(void)
 bool
 config_get_enable_rsync(void)
 {
-       return rpki_config.enable_rsync;
+       return !rpki_config.disable_rsync;
 }
 
 bool
@@ -602,18 +567,13 @@ config_get_max_cert_depth(void)
        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);
+       struct group_fields const *group;
+       struct option_field const *option;
+
+       FOREACH_OPTION(groups, group, option, 0xFFFF)
+               if (is_rpki_config_field(option) && option->type->free != NULL)
+                       option->type->free(get_rpki_config_field(option));
 }
index 14fac274c761e5cc2b06f8d83e723fca0c878b87..71fccefd3d311918f64ba2c630a5103a1ca4ec30 100644 (file)
 
 struct rpki_config;
 
+struct group_fields;
 struct option_field;
 
-typedef enum global_type_id {
-       GTI_BOOL,
-       GTI_STRING,
-       GTI_U_INT,
-} global_type_id;
-
-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;
-};
+typedef void (*print_function)(struct group_fields const *,
+    struct option_field const *, void *);
+typedef int (*parse_function)(struct option_field const *, char const *,
+    void *);
+typedef int (*handler_function)(struct option_field const *, char *);
 
 struct global_type {
-       global_type_id id;
-       const char *name;
+       /** Same as struct option.has_arg. Mandatory. */
+       int has_arg;
+       /**
+        * Number of bytes this data type uses in the rpki_config structure.
+        * Optional. Defaults to zero, obviously.
+        */
        size_t size;
+
+       /**
+        * Prints this data type during the print_config() function.
+        * Optional.
+        */
        print_function print;
+       /**
+        * Convers from string to this data type.
+        * If the option's handler is not NULL, this is optional.
+        */
        parse_function parse;
-       validate_function validate;
-       char *candidates;
+       /**
+        * Function that will release this data type.
+        * If the option's handler is not NULL, this is optional.
+        */
+       void (*free)(void *);
+
+       /**
+        * Descriptor of this type's payload. Printed in usage documentation.
+        * For example, in `--tal=<file>`, @arg_doc is "<file>".
+        * The type might have no payload, so this is optional.
+        */
+       char const *arg_doc;
 };
 
+/** This option can be set from the command line. */
+#define AVAILABILITY_GETOPT (1 << 0)
+/** This option can be set from the TOML file. */
+#define AVAILABILITY_TOML (1 << 1)
+
 struct option_field {
-       char *name; /* This being NULL means the end of the array. */
-       struct global_type *type;
-       const char *doc;
+       /*
+        * Must be zero, alphanumeric or >= 1000.
+        * If zero, signals the end of the array.
+        * If alphanumeric, it's the short option name character.
+        * Otherwise it's just a non-printable identifier.
+        * Mandatory.
+        */
+       int id;
+       /**
+        * For example, if the option name is '--potato', then @name is
+        * "potato".
+        * Mandatory.
+        */
+       char const *name;
+
+       /** Data type. Mandatory. */
+       struct global_type const *type;
+       /**
+        * Number of bytes between the beginning of the struct rpki_config
+        * and the position where this option is stored.
+        * Only relevant when @handler == NULL.
+        */
        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;
+       /** Overrides @type->parser and @offset. Optional. */
+       handler_function handler;
+
+       /**
+        * Explanation of the field, for user consumption.
+        * Mandatory.
+        */
+       const char *doc;
+       /** Overrides type->arg_doc. Optional. */
+       char const *arg_doc;
+       /**
+        * AVAILABILITY_* flags above.
+        * Default availability is everywhere.
+        * Optional.
+        */
+       int availability;
+       unsigned int min;
+       unsigned int max;
 };
 
 struct group_fields {
-       char *group_name;
-       struct option_field *options;
-       unsigned int options_len;
+       char const *name;
+       struct option_field const *options;
 };
 
-void print_usage(char *progname);
-int handle_option(struct rpki_config *, struct option_field *, char *);
+int parse_option(struct option_field const *, char const *);
 int handle_flags_config(int , char **);
 
-void get_group_fields(struct group_fields **);
-
-void config_set(struct rpki_config *);
+void get_group_fields(struct group_fields const **);
 
 char const *config_get_tal(void);
 char const *config_get_local_repository(void);
index cfc06c42246bbd13a16e980809f53a2de33adf23..317aefd18f5436762c8a63b945f3292d0612051f 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -23,20 +23,20 @@ pr_file_name(FILE *stream)
 {
 #ifndef UNIT_TESTING
        char const *file = fnstack_peek();
-       fprintf(stream, "%s: ", (file != NULL) ? file : "(Unknown file)");
+       if (file == NULL)
+               return;
+       fprintf(stream, "%s: ", file);
 #endif
 }
 
-#ifdef DEBUG
-
-static void
-pr_add_indent(void)
+void
+pr_indent_add(void)
 {
        indent++;
 }
 
-static void
-pr_rm_indent(void)
+void
+pr_indent_rm(void)
 {
        if (indent > 0)
                indent--;
@@ -44,6 +44,7 @@ pr_rm_indent(void)
                fprintf(STDERR, "Programming error: Too many pr_rm_indent()s.\n");
 }
 
+#ifdef DEBUG
 
 void
 pr_debug_prefix(void)
@@ -77,7 +78,7 @@ pr_debug_add(const char *format, ...)
        va_end(args);
        fprintf(STDOUT, "\n");
 
-       pr_add_indent();
+       pr_indent_add();
 }
 
 void
@@ -85,7 +86,7 @@ pr_debug_rm(const char *format, ...)
 {
        va_list args;
 
-       pr_rm_indent();
+       pr_indent_rm();
 
        pr_debug_prefix();
 
@@ -113,6 +114,14 @@ pr_prefix(char const *level)
        va_end(args);                           \
 } while (0)
 
+void
+pr_info(const char *format, ...)
+{
+       va_list args;
+       PR_PREFIX("INF", args);
+       fprintf(STDOUT, "\n");
+}
+
 /**
  * Always appends a newline at the end. Always returs 0. (So you can interrupt
  * whatever you're doing without failing validation.)
index 43ead6109c27b0af2726899ab0d08a52c1a7c6e1..90b1b0e3a80f6d743f7c7f53095233f8dff64993 100644 (file)
--- a/src/log.h
+++ b/src/log.h
@@ -21,6 +21,9 @@
 #define CHECK_FORMAT(str, args) /* Nothing */
 #endif
 
+void pr_indent_add(void);
+void pr_indent_rm(void);
+
 #ifdef DEBUG
 
 void pr_debug(const char *, ...) CHECK_FORMAT(1, 2);
@@ -37,6 +40,7 @@ void pr_debug_prefix(void);
 
 #endif
 
+void pr_info(const char *, ...) CHECK_FORMAT(1, 2);
 int pr_warn(const char *, ...) CHECK_FORMAT(1, 2);
 int pr_err(const char *, ...) CHECK_FORMAT(1, 2);
 int pr_errno(int, const char *, ...) CHECK_FORMAT(2, 3);
index 8199199463aedeba4221ded21a525257909cf51a..82f733ad53f7b4d209627af19df42524a6d4890a 100644 (file)
@@ -85,6 +85,9 @@ main(int argc, char **argv)
 
        print_stack_trace_on_segfault();
 
+       thvar_init();
+       fnstack_store();
+
        error = handle_flags_config(argc, argv);
        if (error)
                return error;
@@ -99,8 +102,6 @@ main(int argc, char **argv)
        error = extension_init();
        if (error)
                goto end2;
-       thvar_init();
-       fnstack_store();
        fnstack_push(config_get_tal());
 
        error = tal_load(config_get_tal(), &tal);
index 5decf801bd47424928581c3b64308280a9c80f19..408f48a10bb8cb2bb2d113b1dc2b54646cbdb587 100644 (file)
--- a/src/nid.c
+++ b/src/nid.c
@@ -22,7 +22,7 @@ register_oid(const char *oid, const char *sn, const char *ln)
        if (nid == 0)
                return crypto_err("Unable to register the %s NID.", sn);
 
-       printf("%s registered. Its nid is %d.\n", sn, nid);
+       pr_info("%s registered. Its nid is %d.", sn, nid);
        return nid;
 }
 
index 3b83de779f544c721ceb28f03f2b8869b239fea5..bf47ee521fe97d5b3c9508a7619bea98a4005be1 100644 (file)
@@ -155,8 +155,10 @@ tal_load(char const *file_name, struct tal **result)
        int error;
 
        error = lfile_open(file_name, &lfile);
-       if (error)
+       if (error) {
+               pr_errno(error, "Error opening file '%s'", file_name);
                goto fail4;
+       }
 
        tal = malloc(sizeof(struct tal));
        if (tal == NULL) {
index 1f793104b3b02882ee0e82b8805fa58453158f54..07f7b4194231b7c6b620d1c9988515b8eaf6e142 100644 (file)
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
 #include <toml.h>
 
+#include "config.h"
 #include "file.h"
 #include "log.h"
-#include "thread_var.h"
 #include "uri.h"
 
 static int
-find_flag(struct args_flag *flags_handled, char *flag_to_find,
-    struct args_flag **result)
-{
-       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, struct args_flag *flags_handled)
+toml_to_config(struct toml_table_t *root)
 {
-       struct option_field *tmp_field;
-       struct args_flag *tmp_arg;
-       const char *result;
-       char *str;
+       struct group_fields const *groups;
+       struct group_fields const *group;
+       struct option_field const *option;
+       struct toml_table_t *table;
+       const char *value;
        int error;
 
-       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;
-               }
+       get_group_fields(&groups);
 
-               result = toml_raw_in(table, tmp_field->name);
-               if (result == 0) {
-                       tmp_field += 1;
+       for (group = groups; group->name != NULL; group++) {
+               table = toml_table_in(root, group->name);
+               if (table == NULL)
                        continue;
-               }
-
-               str = (char *) result;
-               if (tmp_field->type->id == GTI_STRING) {
-                       error = toml_rtos(result, &str);
-                       if (error)
-                               return pr_err("Bad value in '%s'",
-                                   tmp_field->name);
-               }
-
-               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;
-       }
-
-       return error;
-}
-
-static int
-toml_to_config(struct toml_table_t *root, struct rpki_config *config,
-    struct args_flag *flags_handled)
-{
-       struct toml_table_t *toml_table;
-       struct group_fields *group_fields;
-       int error;
-
-       get_group_fields(&group_fields);
-       if (group_fields == NULL)
-               return 0;
-
-       error = iterate_fields(root, config, group_fields->options,
-           flags_handled);
-       if (error)
-               return error;
-       group_fields += 1;
-
-       while (group_fields->group_name != NULL) {
-               toml_table = toml_table_in(root, group_fields->group_name);
-               if (toml_table == 0) {
-                       group_fields += 1;
-                       continue;
+               for (option = group->options; option->id != 0; option++) {
+                       if (option->availability == 0
+                           || (option->availability & AVAILABILITY_TOML)) {
+                               value = toml_raw_in(table, option->name);
+                               if (value == NULL)
+                                       continue;
+                               error = parse_option(option, value);
+                               if (error)
+                                       return error;
+                       }
                }
-               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,
-    struct args_flag *flags_handled)
+set_config_from_file(char *config_file)
 {
        FILE *file;
        struct stat stat;
@@ -127,40 +51,26 @@ set_config_from_file(char *config_file, struct rpki_config *config,
        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_open(&uri, &file, &stat);
        if (error)
-               goto end; /* Error msg already printed. */
+               return error; /* Error msg already printed. */
 
        root = toml_parse_file(file, errbuf, sizeof(errbuf));
        file_close(file);
 
        if (root == NULL) {
-               error = pr_err("Error while parsing configuration file: %s",
+               return pr_err("Error while parsing configuration file: %s",
                    errbuf);
-               goto end;
        }
 
-       error = toml_to_config(root, config, flags_handled);
+       error = toml_to_config(root);
 
        toml_free(root);
 
-end:
        return error;
 }
index ae04120a484127a62617fc3d6c6bac47ea85f464..75136aaf492b90405dfba267c36f3105d765f0fc 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef SRC_TOML_HANDLER_H_
 #define SRC_TOML_HANDLER_H_
 
-#include "config.h"
-
-int set_config_from_file(char *, struct rpki_config *, struct args_flag *);
+int set_config_from_file(char *);
 
 #endif /* SRC_TOML_HANDLER_H_ */