]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Refactor configuration types to their own modules
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Thu, 7 Mar 2019 00:13:57 +0000 (18:13 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Thu, 7 Mar 2019 00:13:57 +0000 (18:13 -0600)
Scales a little better, removes clutter from config.c.

19 files changed:
src/Makefile.am
src/config.c
src/config.h
src/config/boolean.c [new file with mode: 0644]
src/config/boolean.h [new file with mode: 0644]
src/config/filename_format.c [new file with mode: 0644]
src/config/filename_format.h [new file with mode: 0644]
src/config/out_file.c [new file with mode: 0644]
src/config/out_file.h [new file with mode: 0644]
src/config/str.c [new file with mode: 0644]
src/config/str.h [new file with mode: 0644]
src/config/string_array.c [new file with mode: 0644]
src/config/string_array.h [new file with mode: 0644]
src/config/sync_strategy.c [new file with mode: 0644]
src/config/sync_strategy.h [new file with mode: 0644]
src/config/types.h [new file with mode: 0644]
src/config/uint.c [new file with mode: 0644]
src/config/uint.h [new file with mode: 0644]
src/toml_handler.c

index 0d521a449a3052ab612b8c67d6ba5d3671a6083a..c4bb88b7c5698fce6c39684239907ba69587e9be 100644 (file)
@@ -36,6 +36,15 @@ rpki_validator_SOURCES += asn1/decode.h asn1/decode.c
 rpki_validator_SOURCES += asn1/oid.h asn1/oid.c
 rpki_validator_SOURCES += asn1/signed_data.h asn1/signed_data.c
 
+rpki_validator_SOURCES += config/boolean.c config/boolean.h
+rpki_validator_SOURCES += config/filename_format.h config/filename_format.c
+rpki_validator_SOURCES += config/out_file.h config/out_file.c
+rpki_validator_SOURCES += config/str.c config/str.h
+rpki_validator_SOURCES += config/string_array.h config/string_array.c
+rpki_validator_SOURCES += config/sync_strategy.h config/sync_strategy.c
+rpki_validator_SOURCES += config/types.h
+rpki_validator_SOURCES += config/uint.c config/uint.h
+
 rpki_validator_SOURCES += crypto/base64.h crypto/base64.c
 rpki_validator_SOURCES += crypto/hash.h crypto/hash.c
 
index 3462739d94eb886b14b551d4d13ac7853966b981..cc21304035d4ccc4c6459c828b1249da34f01333 100644 (file)
@@ -1,7 +1,6 @@
 #include "config.h"
 
 #include <limits.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
@@ -10,6 +9,10 @@
 #include "common.h"
 #include "log.h"
 #include "toml_handler.h"
+#include "config/boolean.h"
+#include "config/out_file.h"
+#include "config/str.h"
+#include "config/uint.h"
 
 /**
  * Please note that this is actually two `for`s stacked together, so don't use
                        if ((opt->availability == 0) ||         \
                            (opt->availability & type))
 
-struct config_out_file {
-       FILE *fd;
-       char *file_name;
-};
-
 /**
  * To add a member to this structure,
  *
@@ -71,47 +69,6 @@ struct rpki_config {
 
 static void print_usage(FILE *, bool);
 
-#define DECLARE_PRINT_FN(name)                                         \
-       static void name(                                               \
-           struct group_fields const *,                                \
-           struct option_field const *,                                \
-           void *                                                      \
-       )
-DECLARE_PRINT_FN(print_bool);
-DECLARE_PRINT_FN(print_u_int);
-DECLARE_PRINT_FN(print_string);
-DECLARE_PRINT_FN(print_string_array);
-DECLARE_PRINT_FN(print_sync_strategy);
-DECLARE_PRINT_FN(print_filename_format);
-DECLARE_PRINT_FN(print_out_file);
-
-#define DECLARE_PARSE_ARGV_FN(name)                                    \
-       static int name(                                                \
-           struct option_field const *,                                \
-           char const *,                                               \
-           void *                                                      \
-       )
-DECLARE_PARSE_ARGV_FN(parse_argv_bool);
-DECLARE_PARSE_ARGV_FN(parse_argv_u_int);
-DECLARE_PARSE_ARGV_FN(parse_argv_string);
-DECLARE_PARSE_ARGV_FN(parse_argv_sync_strategy);
-DECLARE_PARSE_ARGV_FN(parse_argv_filename_format);
-DECLARE_PARSE_ARGV_FN(parse_argv_out_file);
-
-#define DECLARE_PARSE_TOML_FN(name)                                    \
-       static int name(                                                \
-           struct option_field const *,                                \
-           struct toml_table_t *,                                      \
-           void *                                                      \
-       )
-DECLARE_PARSE_TOML_FN(parse_toml_bool);
-DECLARE_PARSE_TOML_FN(parse_toml_u_int);
-DECLARE_PARSE_TOML_FN(parse_toml_string);
-DECLARE_PARSE_TOML_FN(parse_toml_sync_strategy);
-DECLARE_PARSE_TOML_FN(parse_toml_string_array);
-DECLARE_PARSE_TOML_FN(parse_toml_filename_format);
-DECLARE_PARSE_TOML_FN(parse_toml_out_file);
-
 #define DECLARE_HANDLE_FN(name)                                                \
        static int name(                                                \
            struct option_field const *,                                \
@@ -122,79 +79,9 @@ DECLARE_HANDLE_FN(handle_usage);
 DECLARE_HANDLE_FN(handle_version);
 DECLARE_HANDLE_FN(handle_toml);
 
-#define DECLARE_FREE_FN(name) static void name(void *)
-DECLARE_FREE_FN(free_string);
-DECLARE_FREE_FN(free_string_array);
-DECLARE_FREE_FN(free_out_file);
-
 static char const *program_name;
 static struct rpki_config rpki_config;
 
-static const struct global_type gt_bool = {
-       .has_arg = no_argument,
-       .size = sizeof(bool),
-       .print = print_bool,
-       .parse.argv = parse_argv_bool,
-       .parse.toml = parse_toml_bool,
-       .arg_doc = "true|false",
-};
-
-static const struct global_type gt_u_int = {
-       .has_arg = required_argument,
-       .size = sizeof(unsigned int),
-       .print = print_u_int,
-       .parse.argv = parse_argv_u_int,
-       .parse.toml = parse_toml_u_int,
-       .arg_doc = "<unsigned integer>",
-};
-
-static const struct global_type gt_string = {
-       .has_arg = required_argument,
-       .size = sizeof(char *),
-       .print = print_string,
-       .parse.argv = parse_argv_string,
-       .parse.toml = parse_toml_string,
-       .free = free_string,
-       .arg_doc = "<string>",
-};
-
-static const struct global_type gt_string_array = {
-       .has_arg = required_argument,
-       .size = sizeof(char *const *),
-       .print = print_string_array,
-       .parse.toml = parse_toml_string_array,
-       .free = free_string_array,
-       .arg_doc = "<sequence of strings>",
-};
-
-static const struct global_type gt_sync_strategy = {
-       .has_arg = required_argument,
-       .size = sizeof(enum sync_strategy),
-       .print = print_sync_strategy,
-       .parse.argv = parse_argv_sync_strategy,
-       .parse.toml = parse_toml_sync_strategy,
-       .arg_doc = SYNC_VALUE_OFF "|" SYNC_VALUE_STRICT "|" SYNC_VALUE_ROOT,
-};
-
-static const struct global_type gt_filename_format = {
-       .has_arg = required_argument,
-       .size = sizeof(enum filename_format),
-       .print = print_filename_format,
-       .parse.argv = parse_argv_filename_format,
-       .parse.toml = parse_toml_filename_format,
-       .arg_doc = FNF_VALUE_GLOBAL "|" FNF_VALUE_LOCAL "|" FNF_VALUE_NAME,
-};
-
-static const struct global_type gt_out_file = {
-       .has_arg = required_argument,
-       .size = sizeof(struct config_out_file),
-       .print = print_out_file,
-       .parse.argv = parse_argv_out_file,
-       .parse.toml = parse_toml_out_file,
-       .free = free_out_file,
-       .arg_doc = "<file>",
-};
-
 /**
  * An option that takes no arguments, is not correlated to any rpki_config
  * fields, and is entirely managed by its handler function.
@@ -358,388 +245,6 @@ get_rpki_config_field(struct option_field const *field)
        return ((unsigned char *) &rpki_config) + field->offset;
 }
 
-static 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");
-}
-
-static 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));
-}
-
-static 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 void
-print_string_array(struct group_fields const *group,
-    struct option_field const *field, void *_value)
-{
-       struct string_array *value = _value;
-       size_t i;
-
-       pr_info("%s.%s:", group->name, field->name);
-       pr_indent_add();
-
-       if (value->length == 0)
-               pr_info("<Nothing>");
-       else for (i = 0; i < value->length; i++)
-               pr_info("%s", value->array[i]);
-
-       pr_indent_rm();
-}
-
-static void
-print_sync_strategy(struct group_fields const *group,
-    struct option_field const *field, void *value)
-{
-       enum sync_strategy *strategy = value;
-       char const *str = "<unknown>";
-
-       switch (*strategy) {
-       case SYNC_OFF:
-               str = SYNC_VALUE_OFF;
-               break;
-       case SYNC_STRICT:
-               str = SYNC_VALUE_STRICT;
-               break;
-       case SYNC_ROOT:
-               str = SYNC_VALUE_ROOT;
-               break;
-       }
-
-       pr_info("%s.%s: %s", group->name, field->name, str);
-}
-
-static void
-print_filename_format(struct group_fields const *group,
-    struct option_field const *field, void *value)
-{
-       enum filename_format *format = value;
-       char const *str = "<unknown>";
-
-       switch (*format) {
-       case FNF_GLOBAL:
-               str = FNF_VALUE_GLOBAL;
-               break;
-       case FNF_LOCAL:
-               str = FNF_VALUE_LOCAL;
-               break;
-       case FNF_NAME:
-               str = FNF_VALUE_NAME;
-               break;
-       }
-
-       pr_info("%s.%s: %s", group->name, field->name, str);
-}
-
-static void
-print_out_file(struct group_fields const *group,
-    struct option_field const *field, void *value)
-{
-       struct config_out_file *file = value;
-       pr_info("%s.%s: %s", group->name, field->name, file->file_name);
-}
-
-static int
-parse_argv_bool(struct option_field const *field, char const *str, void *result)
-{
-       bool *value = result;
-
-       if (str == NULL) {
-               *value = true;
-               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
-parse_argv_u_int(struct option_field const *field, char const *str, void *_result)
-{
-       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 = strtoul(str, NULL, 10);
-       if (errno)
-               return pr_errno(errno, "'%s' is not an unsigned integer", str);
-
-       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
-parse_argv_string(struct option_field const *field, char const *str, void *_result)
-{
-       char **result = _result;
-
-       /* Remove the previous value (usually the default). */
-       field->type->free(result);
-
-       if (field->type->has_arg != required_argument || str == NULL) {
-               return pr_err("String options ('%s' in this case) require an argument.",
-                   field->name);
-       }
-
-       /* tomlc99 frees @str early, so work with a copy. */
-       *result = strdup(str);
-       return ((*result) != NULL) ? 0 : pr_enomem();
-}
-
-static int
-parse_argv_sync_strategy(struct option_field const *field, char const *str,
-    void *_result)
-{
-       enum sync_strategy *result = _result;
-
-       if (strcmp(str, SYNC_VALUE_OFF) == 0)
-               *result = SYNC_OFF;
-       else if (strcmp(str, SYNC_VALUE_STRICT) == 0)
-               *result = SYNC_STRICT;
-       else if (strcmp(str, SYNC_VALUE_ROOT) == 0)
-               *result = SYNC_ROOT;
-       else
-               return pr_err("Unknown synchronization strategy: '%s'", str);
-
-       return 0;
-}
-
-static int
-parse_argv_filename_format(struct option_field const *field, char const *str,
-    void *_result)
-{
-       enum filename_format *result = _result;
-
-       if (strcmp(str, FNF_VALUE_GLOBAL) == 0)
-               *result = FNF_GLOBAL;
-       else if (strcmp(str, FNF_VALUE_LOCAL) == 0)
-               *result = FNF_LOCAL;
-       else if (strcmp(str, FNF_VALUE_NAME) == 0)
-               *result = FNF_NAME;
-       else
-               return pr_err("Unknown file name format: '%s'", str);
-
-       return 0;
-}
-
-static int
-parse_argv_out_file(struct option_field const *field, char const *file_name,
-    void *_result)
-{
-       struct config_out_file *file = _result;
-
-       field->type->free(file);
-
-       file->file_name = strdup(file_name);
-       if (file->file_name == NULL)
-               return pr_enomem();
-
-       file->fd = fopen(file_name, "w");
-       if (file->fd == NULL) {
-               free(file->file_name);
-               file->file_name = NULL;
-               return pr_errno(errno, "Could not open file '%s'", file_name);
-       }
-
-       return 0;
-}
-
-static int
-parse_toml_bool(struct option_field const *opt, struct toml_table_t *toml,
-    void *_result)
-{
-       const char *raw;
-       int value;
-       bool *result;
-
-       raw = toml_raw_in(toml, opt->name);
-       if (raw == NULL)
-               return pr_err("TOML boolean '%s' was not found.", opt->name);
-       if (toml_rtob(raw, &value) == -1)
-               return pr_err("Cannot parse '%s' as a boolean.", raw);
-
-       result = _result;
-       *result = value;
-       return 0;
-}
-
-static int
-parse_toml_u_int(struct option_field const *opt, struct toml_table_t *toml,
-    void *_result)
-{
-       const char *raw;
-       int64_t value;
-       unsigned int *result;
-
-       raw = toml_raw_in(toml, opt->name);
-       if (raw == NULL)
-               return pr_err("TOML integer '%s' was not found.", opt->name);
-       if (toml_rtoi(raw, &value) == -1)
-               return pr_err("Cannot parse '%s' as an integer.", raw);
-
-       if (value < opt->min || opt->max < value) {
-               return pr_err("Integer '%s' is out of range (%u-%u).",
-                   opt->name, opt->min, opt->max);
-       }
-
-       result = _result;
-       *result = value;
-       return 0;
-}
-
-static int
-parse_toml_string(struct option_field const *opt, struct toml_table_t *toml,
-    void *_result)
-{
-       const char *raw;
-       char *value;
-       char **result;
-
-       /* Remove the previous value (usually the default). */
-       opt->type->free(_result);
-
-       raw = toml_raw_in(toml, opt->name);
-       if (raw == NULL)
-               return pr_err("TOML string '%s' was not found.", opt->name);
-       if (toml_rtos(raw, &value) == -1)
-               return pr_err("Cannot parse '%s' as a string.", raw);
-
-       result = _result;
-       *result = value;
-       return 0;
-}
-
-static int
-parse_toml_sync_strategy(struct option_field const *opt,
-    struct toml_table_t *toml, void *_result)
-{
-       int error;
-       char *string;
-
-       string = NULL;
-       error = parse_toml_string(opt, toml, &string);
-       if (error)
-               return error;
-
-       error = parse_argv_sync_strategy(opt, string, _result);
-
-       free(string);
-       return error;
-}
-
-static int
-parse_toml_string_array(struct option_field const *opt,
-    struct toml_table_t *toml, void *_result)
-{
-       toml_array_t *array;
-       int array_len;
-       int i;
-       const char *raw;
-       struct string_array *result = _result;
-       int error;
-
-       /* Remove the previous value (usually the default). */
-       opt->type->free(_result);
-
-       array = toml_array_in(toml, opt->name);
-       if (array == NULL)
-               return pr_err("TOML array '%s' was not found.", opt->name);
-       array_len = toml_array_nelem(array);
-
-       result->array = malloc(array_len * sizeof(char *));
-       if (result->array == NULL)
-               return pr_enomem();
-       result->length = array_len;
-
-       for (i = 0; i < array_len; i++) {
-               raw = toml_raw_at(array, i);
-               if (raw == NULL) {
-                       error = pr_crit("Array index %d is NULL.", i);
-                       goto fail;
-               }
-               if (toml_rtos(raw, &result->array[i]) == -1) {
-                       error = pr_err("Cannot parse '%s' as a string.", raw);
-                       goto fail;
-               }
-       }
-
-       return 0;
-
-fail:
-       free(result->array);
-       result->length = 0;
-       return error;
-}
-
-static int
-parse_toml_filename_format(struct option_field const *opt,
-    struct toml_table_t *toml, void *_result)
-{
-       int error;
-       char *string;
-
-       string = NULL;
-       error = parse_toml_string(opt, toml, &string);
-       if (error)
-               return error;
-
-       error = parse_argv_filename_format(opt, string, _result);
-
-       free(string);
-       return error;
-}
-
-static int
-parse_toml_out_file(struct option_field const *opt, struct toml_table_t *toml,
-    void *_result)
-{
-       char *file_name;
-       int error;
-
-       file_name = NULL;
-       error = parse_toml_string(opt, toml, &file_name);
-       if (error)
-               return error;
-
-       error = parse_argv_out_file(opt, file_name, _result);
-
-       free(file_name);
-       return error;
-}
-
 static int
 handle_help(struct option_field const *field, char *arg)
 {
@@ -767,42 +272,6 @@ handle_toml(struct option_field const *field, char *file_name)
        return set_config_from_file(file_name);
 }
 
-static void
-free_string(void *_string)
-{
-       char **string = _string;
-       free(*string);
-       *string = NULL;
-}
-
-static void
-free_string_array(void *_array)
-{
-       struct string_array *array = _array;
-       size_t i;
-
-       for (i = 0; i < array->length; i++)
-               free(array->array[i]);
-       free(array->array);
-
-       array->array = NULL;
-       array->length = 0;
-}
-
-static void
-free_out_file(void *_file)
-{
-       struct config_out_file *file = _file;
-
-       if (file->fd != NULL) {
-               fclose(file->fd);
-               file->fd = NULL;
-       }
-
-       free(file->file_name);
-       file->file_name = NULL;
-}
-
 static bool
 is_alphanumeric(int chara)
 {
index a21e69def85aceb7ca95c9b8269ab9b4efbd5ad9..c2d755d1fe7564e92092b0d2a8aba8495c2db371 100644 (file)
 #define SRC_CONFIG_H_
 
 #include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <toml.h>
-#include <openssl/bio.h>
 
-/**
- * Note: The only repository synchronization protocol implemented so far is
- * RSYNC. Whenever you see "sync", think "rsync."
- */
-enum sync_strategy {
-       /**
-        * Synchronization is turned off.
-        * The validator will work on an already downloaded repository.
-        */
-       SYNC_OFF,
-       /**
-        * Strictly correct download strategy.
-        *
-        * The validator will sync each repository publication point separately
-        * as requested by each caRepository contained in the CA certificates'
-        * SIA extensions.
-        *
-        * No risk of downloading unneeded files, but otherwise slow, as every
-        * different repository publication point requires a separate sync call.
-        */
-       SYNC_STRICT,
-       /**
-        * Always download the likely root of the entire repository.
-        *
-        * For example, if we get the following caRepositories:
-        *
-        * - `rsync://a.b.c/d/e/f/g/h/i`
-        * - `rsync://a.b.c/d/e/f/g/h/j`
-        * - `rsync://a.b.c/d/e/f/k`
-        *
-        * This strategy will synchronize `rsync://a.b.c/d` while parsing the
-        * first caRepository, and then skip synchronization during the second
-        * and third ones. (Because they are already downloaded.)
-        *
-        * This strategy risks downloading unneeded files, and even failing due
-        * to lack of read permissions on stray subdirectories. On the flip
-        * side, if the repository holds no unnecessary subdirectories, then
-        * this strategy is the fastest one, since it generally only requires
-        * one sync call per domain, which often translates into one sync call
-        * per validation cycle.
-        *
-        * Currently, all of the official repositories are actually specifically
-        * structured to benefit this strategy.
-        */
-       SYNC_ROOT,
-};
+#include "config/filename_format.h"
+#include "config/sync_strategy.h"
+#include "config/string_array.h"
+#include "config/types.h"
 
-#define SYNC_VALUE_OFF         "off"
-#define SYNC_VALUE_STRICT      "strict"
-#define SYNC_VALUE_ROOT                "root"
-
-enum filename_format {
-       /** Example: "rsync://repository.lacnic.net/rpki/foo/bar/baz.cer" */
-       FNF_GLOBAL,
-       /** Example: "/tmp/repo/repository.lacnic.net/rpki/foo/bar/baz.cer" */
-       FNF_LOCAL,
-       /** Example: "baz.cer" */
-       FNF_NAME,
-};
-
-#define FNF_VALUE_GLOBAL "global-url"
-#define FNF_VALUE_LOCAL "local-path"
-#define FNF_VALUE_NAME "file-name"
-
-struct rpki_config;
-
-struct group_fields;
-struct option_field;
-
-typedef void (*print_function)(
-    struct group_fields const *,
-    struct option_field const *,
-    void *
-);
-typedef int (*argv_parse_function)(
-    struct option_field const *,
-    char const *,
-    void *
-);
-typedef int (*toml_parse_function)(
-    struct option_field const *,
-    struct toml_table_t *,
-    void *
-);
-typedef int (*handler_function)(
-    struct option_field const *,
-    char *
-);
-
-struct global_type {
-       /** 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;
-
-       /** If the option's handler is not NULL, this is optional. */
-       struct {
-               /**
-                * Convers from string to this data type.
-                * Optional if there are no fields of this type that are read
-                * from argv.
-                */
-               argv_parse_function argv;
-               /**
-                * Converts from a TOML node to this data type.
-                * Optional if there are no fields of this type that are read
-                * from TOML files.
-                */
-               toml_parse_function toml;
-       } parse;
-
-       /**
-        * Function that will release this data type.
-        * If the option's handler is not NULL, this is optional.
-        *
-        * IMPORTANT: This function might be called twice in succession.
-        * Therefore, make sure that it nullifies the value, and reacts properly
-        * when the input is NULL.
-        */
-       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 {
-       /*
-        * 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.
-        * Must be unique across all option fields.
-        * 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;
-       /** Overrides @type->parser and @offset. Optional. */
-       handler_function handler;
-
-       /**
-        * Explanation of the field, for user consumption during --help.
-        * Meant to be short; the bulk of it should be found in the manpage.
-        * Probably should not include punctuation at the end.
-        * 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 const *name;
-       struct option_field const *options;
-};
-
-struct string_array {
-       char **array;
-       size_t length;
-};
-
-void *get_rpki_config_field(struct option_field const *);
+/* Init/destroy */
 int handle_flags_config(int , char **);
+void free_rpki_config(void);
 
-void get_group_fields(struct group_fields const **);
-
+/* Getters */
 char const *config_get_tal(void);
 char const *config_get_local_repository(void);
 enum sync_strategy config_get_sync_strategy(void);
@@ -224,6 +24,8 @@ FILE *config_get_roa_output(void);
 char *config_get_rsync_program(void);
 struct string_array const *config_get_rsync_args(void);
 
-void free_rpki_config(void);
+/* Needed public by the TOML module */
+void *get_rpki_config_field(struct option_field const *);
+void get_group_fields(struct group_fields const **);
 
 #endif /* SRC_CONFIG_H_ */
diff --git a/src/config/boolean.c b/src/config/boolean.c
new file mode 100644 (file)
index 0000000..c71e924
--- /dev/null
@@ -0,0 +1,66 @@
+#include "config/boolean.h"
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <string.h>
+#include "log.h"
+
+static 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");
+}
+
+static int
+parse_argv_bool(struct option_field const *field, char const *str, void *result)
+{
+       bool *value = result;
+
+       if (str == NULL) {
+               *value = true;
+               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
+parse_toml_bool(struct option_field const *opt, struct toml_table_t *toml,
+    void *_result)
+{
+       const char *raw;
+       int value;
+       bool *result;
+
+       raw = toml_raw_in(toml, opt->name);
+       if (raw == NULL)
+               return pr_err("TOML boolean '%s' was not found.", opt->name);
+       if (toml_rtob(raw, &value) == -1)
+               return pr_err("Cannot parse '%s' as a boolean.", raw);
+
+       result = _result;
+       *result = value;
+       return 0;
+}
+
+const struct global_type gt_bool = {
+       .has_arg = no_argument,
+       .size = sizeof(bool),
+       .print = print_bool,
+       .parse.argv = parse_argv_bool,
+       .parse.toml = parse_toml_bool,
+       .arg_doc = "true|false",
+};
diff --git a/src/config/boolean.h b/src/config/boolean.h
new file mode 100644 (file)
index 0000000..8668be6
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef SRC_CONFIG_BOOLEAN_H_
+#define SRC_CONFIG_BOOLEAN_H_
+
+#include "config/types.h"
+
+extern const struct global_type gt_bool;
+
+#endif /* SRC_CONFIG_BOOLEAN_H_ */
diff --git a/src/config/filename_format.c b/src/config/filename_format.c
new file mode 100644 (file)
index 0000000..a2fe251
--- /dev/null
@@ -0,0 +1,79 @@
+#include "config/filename_format.h"
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "config/str.h"
+
+#define FNF_VALUE_GLOBAL "global-url"
+#define FNF_VALUE_LOCAL "local-path"
+#define FNF_VALUE_NAME "file-name"
+
+static void
+print_filename_format(struct group_fields const *group,
+    struct option_field const *field, void *value)
+{
+       enum filename_format *format = value;
+       char const *str = "<unknown>";
+
+       switch (*format) {
+       case FNF_GLOBAL:
+               str = FNF_VALUE_GLOBAL;
+               break;
+       case FNF_LOCAL:
+               str = FNF_VALUE_LOCAL;
+               break;
+       case FNF_NAME:
+               str = FNF_VALUE_NAME;
+               break;
+       }
+
+       pr_info("%s.%s: %s", group->name, field->name, str);
+}
+
+static int
+parse_argv_filename_format(struct option_field const *field, char const *str,
+    void *_result)
+{
+       enum filename_format *result = _result;
+
+       if (strcmp(str, FNF_VALUE_GLOBAL) == 0)
+               *result = FNF_GLOBAL;
+       else if (strcmp(str, FNF_VALUE_LOCAL) == 0)
+               *result = FNF_LOCAL;
+       else if (strcmp(str, FNF_VALUE_NAME) == 0)
+               *result = FNF_NAME;
+       else
+               return pr_err("Unknown file name format: '%s'", str);
+
+       return 0;
+}
+
+static int
+parse_toml_filename_format(struct option_field const *opt,
+    struct toml_table_t *toml, void *_result)
+{
+       char *string;
+       int error;
+
+       string = NULL;
+       error = parse_toml_string(opt, toml, &string);
+       if (error)
+               return error;
+
+       error = parse_argv_filename_format(opt, string, _result);
+
+       free(string);
+       return error;
+}
+
+const struct global_type gt_filename_format = {
+       .has_arg = required_argument,
+       .size = sizeof(enum filename_format),
+       .print = print_filename_format,
+       .parse.argv = parse_argv_filename_format,
+       .parse.toml = parse_toml_filename_format,
+       .arg_doc = FNF_VALUE_GLOBAL "|" FNF_VALUE_LOCAL "|" FNF_VALUE_NAME,
+};
diff --git a/src/config/filename_format.h b/src/config/filename_format.h
new file mode 100644 (file)
index 0000000..9074851
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef SRC_CONFIG_FILENAME_FORMAT_H_
+#define SRC_CONFIG_FILENAME_FORMAT_H_
+
+#include "config/types.h"
+
+enum filename_format {
+       /** Example: "rsync://repository.lacnic.net/rpki/foo/bar/baz.cer" */
+       FNF_GLOBAL,
+       /** Example: "/tmp/repo/repository.lacnic.net/rpki/foo/bar/baz.cer" */
+       FNF_LOCAL,
+       /** Example: "baz.cer" */
+       FNF_NAME,
+};
+
+extern const struct global_type gt_filename_format;
+
+#endif /* SRC_CONFIG_FILENAME_FORMAT_H_ */
diff --git a/src/config/out_file.c b/src/config/out_file.c
new file mode 100644 (file)
index 0000000..b701322
--- /dev/null
@@ -0,0 +1,83 @@
+#include "config/out_file.h"
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "config/str.h"
+
+static void
+print_out_file(struct group_fields const *group,
+    struct option_field const *field, void *value)
+{
+       struct config_out_file *file = value;
+       pr_info("%s.%s: %s", group->name, field->name, file->file_name);
+}
+
+static int
+parse_argv_out_file(struct option_field const *field, char const *file_name,
+    void *_result)
+{
+       struct config_out_file *file = _result;
+       int error;
+
+       field->type->free(file);
+
+       file->file_name = strdup(file_name);
+       if (file->file_name == NULL)
+               return pr_enomem();
+
+       file->fd = fopen(file_name, "w");
+       if (file->fd == NULL) {
+               error = errno;
+               free(file->file_name);
+               file->file_name = NULL;
+               return pr_errno(error, "Could not open file '%s'", file_name);
+       }
+
+       return 0;
+}
+
+static int
+parse_toml_out_file(struct option_field const *opt, struct toml_table_t *toml,
+    void *_result)
+{
+       char *file_name;
+       int error;
+
+       file_name = NULL;
+       error = parse_toml_string(opt, toml, &file_name);
+       if (error)
+               return error;
+
+       error = parse_argv_out_file(opt, file_name, _result);
+
+       free(file_name);
+       return error;
+}
+
+static void
+free_out_file(void *_file)
+{
+       struct config_out_file *file = _file;
+
+       if (file->fd != NULL) {
+               fclose(file->fd);
+               file->fd = NULL;
+       }
+
+       free(file->file_name);
+       file->file_name = NULL;
+}
+
+const struct global_type gt_out_file = {
+       .has_arg = required_argument,
+       .size = sizeof(struct config_out_file),
+       .print = print_out_file,
+       .parse.argv = parse_argv_out_file,
+       .parse.toml = parse_toml_out_file,
+       .free = free_out_file,
+       .arg_doc = "<file>",
+};
diff --git a/src/config/out_file.h b/src/config/out_file.h
new file mode 100644 (file)
index 0000000..68665b2
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef SRC_CONFIG_OUT_FILE_H_
+#define SRC_CONFIG_OUT_FILE_H_
+
+#include <stdio.h>
+#include "config/types.h"
+
+struct config_out_file {
+       FILE *fd;
+       char *file_name;
+};
+
+extern const struct global_type gt_out_file;
+
+#endif /* SRC_CONFIG_OUT_FILE_H_ */
diff --git a/src/config/str.c b/src/config/str.c
new file mode 100644 (file)
index 0000000..6ed2467
--- /dev/null
@@ -0,0 +1,72 @@
+#include "config/str.h"
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include "log.h"
+
+static 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_argv_string(struct option_field const *field, char const *str,
+    void *_result)
+{
+       char **result = _result;
+
+       /* Remove the previous value (usually the default). */
+       field->type->free(result);
+
+       if (field->type->has_arg != required_argument || str == NULL) {
+               return pr_err("String options ('%s' in this case) require an argument.",
+                   field->name);
+       }
+
+       /* tomlc99 frees @str early, so work with a copy. */
+       *result = strdup(str);
+       return ((*result) != NULL) ? 0 : pr_enomem();
+}
+
+int
+parse_toml_string(struct option_field const *opt, struct toml_table_t *toml,
+    void *_result)
+{
+       const char *raw;
+       char *value;
+       char **result;
+
+       /* Remove the previous value (usually the default). */
+       opt->type->free(_result);
+
+       raw = toml_raw_in(toml, opt->name);
+       if (raw == NULL)
+               return pr_err("TOML string '%s' was not found.", opt->name);
+       if (toml_rtos(raw, &value) == -1)
+               return pr_err("Cannot parse '%s' as a string.", raw);
+
+       result = _result;
+       *result = value;
+       return 0;
+}
+
+static void
+free_string(void *_string)
+{
+       char **string = _string;
+       free(*string);
+       *string = NULL;
+}
+
+const struct global_type gt_string = {
+       .has_arg = required_argument,
+       .size = sizeof(char *),
+       .print = print_string,
+       .parse.argv = parse_argv_string,
+       .parse.toml = parse_toml_string,
+       .free = free_string,
+       .arg_doc = "<string>",
+};
diff --git a/src/config/str.h b/src/config/str.h
new file mode 100644 (file)
index 0000000..f2cbfc4
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef SRC_CONFIG_STR_H_
+#define SRC_CONFIG_STR_H_
+
+#include "config/types.h"
+
+extern const struct global_type gt_string;
+
+int parse_toml_string(struct option_field const *, struct toml_table_t *,
+    void *);
+
+#endif /* SRC_CONFIG_STR_H_ */
diff --git a/src/config/string_array.c b/src/config/string_array.c
new file mode 100644 (file)
index 0000000..0ee8dd3
--- /dev/null
@@ -0,0 +1,90 @@
+#include "config/string_array.h"
+
+#include <getopt.h>
+#include <stdlib.h>
+#include "log.h"
+
+static void
+print_string_array(struct group_fields const *group,
+    struct option_field const *field, void *_value)
+{
+       struct string_array *value = _value;
+       size_t i;
+
+       pr_info("%s.%s:", group->name, field->name);
+       pr_indent_add();
+
+       if (value->length == 0)
+               pr_info("<Nothing>");
+       else for (i = 0; i < value->length; i++)
+               pr_info("%s", value->array[i]);
+
+       pr_indent_rm();
+}
+
+static int
+parse_toml_string_array(struct option_field const *opt,
+    struct toml_table_t *toml, void *_result)
+{
+       toml_array_t *array;
+       int array_len;
+       int i;
+       const char *raw;
+       struct string_array *result = _result;
+       int error;
+
+       /* Remove the previous value (usually the default). */
+       opt->type->free(_result);
+
+       array = toml_array_in(toml, opt->name);
+       if (array == NULL)
+               return pr_err("TOML array '%s' was not found.", opt->name);
+       array_len = toml_array_nelem(array);
+
+       result->array = malloc(array_len * sizeof(char *));
+       if (result->array == NULL)
+               return pr_enomem();
+       result->length = array_len;
+
+       for (i = 0; i < array_len; i++) {
+               raw = toml_raw_at(array, i);
+               if (raw == NULL) {
+                       error = pr_crit("Array index %d is NULL.", i);
+                       goto fail;
+               }
+               if (toml_rtos(raw, &result->array[i]) == -1) {
+                       error = pr_err("Cannot parse '%s' as a string.", raw);
+                       goto fail;
+               }
+       }
+
+       return 0;
+
+fail:
+       free(result->array);
+       result->length = 0;
+       return error;
+}
+
+static void
+free_string_array(void *_array)
+{
+       struct string_array *array = _array;
+       size_t i;
+
+       for (i = 0; i < array->length; i++)
+               free(array->array[i]);
+       free(array->array);
+
+       array->array = NULL;
+       array->length = 0;
+}
+
+const struct global_type gt_string_array = {
+       .has_arg = required_argument,
+       .size = sizeof(char *const *),
+       .print = print_string_array,
+       .parse.toml = parse_toml_string_array,
+       .free = free_string_array,
+       .arg_doc = "<sequence of strings>",
+};
diff --git a/src/config/string_array.h b/src/config/string_array.h
new file mode 100644 (file)
index 0000000..02c73e2
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef SRC_CONFIG_STRING_ARRAY_H_
+#define SRC_CONFIG_STRING_ARRAY_H_
+
+#include <stddef.h>
+#include "config/types.h"
+
+struct string_array {
+       char **array;
+       size_t length;
+};
+
+extern const struct global_type gt_string_array;
+
+#endif /* SRC_CONFIG_STRING_ARRAY_H_ */
diff --git a/src/config/sync_strategy.c b/src/config/sync_strategy.c
new file mode 100644 (file)
index 0000000..71cfbe9
--- /dev/null
@@ -0,0 +1,79 @@
+#include "config/sync_strategy.h"
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "config/str.h"
+
+#define SYNC_VALUE_OFF         "off"
+#define SYNC_VALUE_STRICT      "strict"
+#define SYNC_VALUE_ROOT                "root"
+
+static void
+print_sync_strategy(struct group_fields const *group,
+    struct option_field const *field, void *value)
+{
+       enum sync_strategy *strategy = value;
+       char const *str = "<unknown>";
+
+       switch (*strategy) {
+       case SYNC_OFF:
+               str = SYNC_VALUE_OFF;
+               break;
+       case SYNC_STRICT:
+               str = SYNC_VALUE_STRICT;
+               break;
+       case SYNC_ROOT:
+               str = SYNC_VALUE_ROOT;
+               break;
+       }
+
+       pr_info("%s.%s: %s", group->name, field->name, str);
+}
+
+static int
+parse_argv_sync_strategy(struct option_field const *field, char const *str,
+    void *_result)
+{
+       enum sync_strategy *result = _result;
+
+       if (strcmp(str, SYNC_VALUE_OFF) == 0)
+               *result = SYNC_OFF;
+       else if (strcmp(str, SYNC_VALUE_STRICT) == 0)
+               *result = SYNC_STRICT;
+       else if (strcmp(str, SYNC_VALUE_ROOT) == 0)
+               *result = SYNC_ROOT;
+       else
+               return pr_err("Unknown synchronization strategy: '%s'", str);
+
+       return 0;
+}
+
+static int
+parse_toml_sync_strategy(struct option_field const *opt,
+    struct toml_table_t *toml, void *_result)
+{
+       int error;
+       char *string;
+
+       string = NULL;
+       error = parse_toml_string(opt, toml, &string);
+       if (error)
+               return error;
+
+       error = parse_argv_sync_strategy(opt, string, _result);
+
+       free(string);
+       return error;
+}
+
+const struct global_type gt_sync_strategy = {
+       .has_arg = required_argument,
+       .size = sizeof(enum sync_strategy),
+       .print = print_sync_strategy,
+       .parse.argv = parse_argv_sync_strategy,
+       .parse.toml = parse_toml_sync_strategy,
+       .arg_doc = SYNC_VALUE_OFF "|" SYNC_VALUE_STRICT "|" SYNC_VALUE_ROOT,
+};
diff --git a/src/config/sync_strategy.h b/src/config/sync_strategy.h
new file mode 100644 (file)
index 0000000..4f00fdd
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef SRC_CONFIG_SYNC_STRATEGY_H_
+#define SRC_CONFIG_SYNC_STRATEGY_H_
+
+#include "config/types.h"
+
+/**
+ * Note: The only repository synchronization protocol implemented so far is
+ * RSYNC. Whenever you see "sync", think "rsync."
+ */
+enum sync_strategy {
+       /**
+        * Synchronization is turned off.
+        * The validator will work on an already downloaded repository.
+        */
+       SYNC_OFF,
+       /**
+        * Strictly correct download strategy.
+        *
+        * The validator will sync each repository publication point separately
+        * as requested by each caRepository contained in the CA certificates'
+        * SIA extensions.
+        *
+        * No risk of downloading unneeded files, but otherwise slow, as every
+        * different repository publication point requires a separate sync call.
+        */
+       SYNC_STRICT,
+       /**
+        * Always download the likely root of the entire repository.
+        *
+        * For example, if we get the following caRepositories:
+        *
+        * - `rsync://a.b.c/d/e/f/g/h/i`
+        * - `rsync://a.b.c/d/e/f/g/h/j`
+        * - `rsync://a.b.c/d/e/f/k`
+        *
+        * This strategy will synchronize `rsync://a.b.c/d` while parsing the
+        * first caRepository, and then skip synchronization during the second
+        * and third ones. (Because they are already downloaded.)
+        *
+        * This strategy risks downloading unneeded files, and even failing due
+        * to lack of read permissions on stray subdirectories. On the flip
+        * side, if the repository holds no unnecessary subdirectories, then
+        * this strategy is the fastest one, since it generally only requires
+        * one sync call per domain, which often translates into one sync call
+        * per validation cycle.
+        *
+        * Currently, all of the official repositories are actually specifically
+        * structured to benefit this strategy.
+        */
+       SYNC_ROOT,
+};
+
+extern const struct global_type gt_sync_strategy;
+
+#endif /* SRC_CONFIG_SYNC_STRATEGY_H_ */
diff --git a/src/config/types.h b/src/config/types.h
new file mode 100644 (file)
index 0000000..c0b1e2f
--- /dev/null
@@ -0,0 +1,137 @@
+#ifndef SRC_CONFIG_TYPES_H_
+#define SRC_CONFIG_TYPES_H_
+
+#include <stdint.h>
+#include <stdio.h>
+#include <toml.h>
+
+struct option_field;
+struct group_fields;
+
+/** 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)
+
+typedef void (*print_function)(
+    struct group_fields const *,
+    struct option_field const *,
+    void *
+);
+typedef int (*argv_parse_function)(
+    struct option_field const *,
+    char const *,
+    void *
+);
+typedef int (*toml_parse_function)(
+    struct option_field const *,
+    struct toml_table_t *,
+    void *
+);
+typedef int (*handler_function)(
+    struct option_field const *,
+    char *
+);
+
+struct option_field {
+       /*
+        * 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.
+        * Must be unique across all option fields.
+        * 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;
+       /** Overrides @type->parser and @offset. Optional. */
+       handler_function handler;
+
+       /**
+        * Explanation of the field, for user consumption during --help.
+        * Meant to be short; the bulk of it should be found in the manpage.
+        * Probably should not include punctuation at the end.
+        * 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 const *name;
+       struct option_field const *options;
+};
+
+struct global_type {
+       /** 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;
+
+       /** If the option's handler is not NULL, this is optional. */
+       struct {
+               /**
+                * Convers from string to this data type.
+                * Optional if there are no fields of this type that are read
+                * from argv.
+                */
+               argv_parse_function argv;
+               /**
+                * Converts from a TOML node to this data type.
+                * Optional if there are no fields of this type that are read
+                * from TOML files.
+                */
+               toml_parse_function toml;
+       } parse;
+
+       /**
+        * Function that will release this data type.
+        * If the option's handler is not NULL, this is optional.
+        *
+        * IMPORTANT: This function might be called twice in succession.
+        * Therefore, make sure that it nullifies the value, and reacts properly
+        * when the input is NULL.
+        */
+       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;
+};
+
+#endif /* SRC_CONFIG_TYPES_H_ */
diff --git a/src/config/uint.c b/src/config/uint.c
new file mode 100644 (file)
index 0000000..702d652
--- /dev/null
@@ -0,0 +1,74 @@
+#include "config/uint.h"
+
+#include <getopt.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "log.h"
+
+static 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));
+}
+
+static int
+parse_argv_u_int(struct option_field const *field, char const *str,
+    void *_result)
+{
+       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 = strtoul(str, NULL, 10);
+       if (errno)
+               return pr_errno(errno, "'%s' is not an unsigned integer", str);
+
+       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
+parse_toml_u_int(struct option_field const *opt, struct toml_table_t *toml,
+    void *_result)
+{
+       const char *raw;
+       int64_t value;
+       unsigned int *result;
+
+       raw = toml_raw_in(toml, opt->name);
+       if (raw == NULL)
+               return pr_err("TOML integer '%s' was not found.", opt->name);
+       if (toml_rtoi(raw, &value) == -1)
+               return pr_err("Cannot parse '%s' as an integer.", raw);
+
+       if (value < opt->min || opt->max < value) {
+               return pr_err("Integer '%s' is out of range (%u-%u).",
+                   opt->name, opt->min, opt->max);
+       }
+
+       result = _result;
+       *result = value;
+       return 0;
+}
+
+const struct global_type gt_u_int = {
+       .has_arg = required_argument,
+       .size = sizeof(unsigned int),
+       .print = print_u_int,
+       .parse.argv = parse_argv_u_int,
+       .parse.toml = parse_toml_u_int,
+       .arg_doc = "<unsigned integer>",
+};
diff --git a/src/config/uint.h b/src/config/uint.h
new file mode 100644 (file)
index 0000000..5623385
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef SRC_CONFIG_UINT_H_
+#define SRC_CONFIG_UINT_H_
+
+#include "config/types.h"
+
+extern const struct global_type gt_u_int;
+
+#endif /* SRC_CONFIG_UINT_H_ */
index b24cb8e4c2b9c6a947942be052045c949cc9d80c..49176307c877106fa86f9d1feb58768bf875036d 100644 (file)
@@ -8,6 +8,7 @@
 #include "file.h"
 #include "log.h"
 #include "uri.h"
+#include "config/types.h"
 
 static int
 toml_to_config(struct toml_table_t *root)