]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Add flag parser framework
authordhfelix <daniel.hdz.felix@hotmail.com>
Fri, 8 Feb 2019 20:26:52 +0000 (14:26 -0600)
committerdhfelix <daniel.hdz.felix@hotmail.com>
Fri, 8 Feb 2019 20:26:52 +0000 (14:26 -0600)
- checkpoint commit
- First version, stable and working but needs some tweaks

src/Makefile.am
src/config.c
src/config.h
src/main.c
src/toml_handler.c [new file with mode: 0644]
src/toml_handler.h [new file with mode: 0644]

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