/* This file is part of Eclat. Copyright (C) 2012-2018 Sergey Poznyakoff. Eclat is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. Eclat is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Eclat. If not, see . */ #include "eclat.h" struct config_finish_hook_entry { struct config_finish_hook_entry *next; config_finish_hook_t fun; void *data; }; static struct config_finish_hook_entry *cfh_head, *cfh_tail; void add_config_finish_hook(config_finish_hook_t fun, void *data) { struct config_finish_hook_entry *ent = grecs_malloc(sizeof(*ent)); ent->next = NULL; ent->fun = fun; ent->data = data; if (cfh_tail) cfh_tail->next = ent; else cfh_head = ent; cfh_tail = ent; } int run_config_finish_hooks() { struct config_finish_hook_entry *p; int rc = 0; for (p = cfh_head; p; p = p->next) rc |= p->fun(p->data); return rc; } /* Translation table: region -> endpoint. Its elements are of struct ec2_param type. */ struct grecs_symtab *ec2_regtab; char * region_to_endpoint(const char *region) { struct ec2_param *p, key; key.name = (char*) region; p = grecs_symtab_lookup_or_install(ec2_regtab, &key, NULL); return p ? p->value : NULL; } static int cb_region(enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { struct ec2_param *p, key; int install = 1; if (cmd != grecs_callback_set_value) { grecs_error(locus, 0, "Unexpected block statement"); return 1; } if (!value || value->type != GRECS_TYPE_ARRAY || value->v.arg.c != 2) { grecs_error(locus, 0, "expected two strings"); return 1; } if (value->v.arg.v[0]->type != GRECS_TYPE_STRING || value->v.arg.v[1]->type != GRECS_TYPE_STRING) { grecs_error(locus, 0, "expected two strings"); return 1; } key.name = value->v.arg.v[0]->v.string; p = grecs_symtab_lookup_or_install(ec2_regtab, &key, &install); if (!install) free(p->value); /* FIXME: Redefinition warning */ p->value = grecs_strdup(value->v.arg.v[1]->v.string); return 0; } static int cb_format(enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { if (cmd != grecs_callback_set_value) { grecs_error(locus, 0, "Unexpected block statement"); return 1; } if (!value || value->type != GRECS_TYPE_ARRAY || value->v.arg.c != 2) { grecs_error(locus, 0, "expected two strings"); return 1; } if (value->v.arg.v[0]->type != GRECS_TYPE_STRING || value->v.arg.v[1]->type != GRECS_TYPE_STRING) { grecs_error(locus, 0, "expected two strings"); return 1; } set_command_format(value->v.arg.v[0]->v.string, value->v.arg.v[1]->v.string, &value->v.arg.v[1]->locus); return 0; } static int cb_define_format(enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { if (cmd != grecs_callback_set_value) { grecs_error(locus, 0, "Unexpected block statement"); return 1; } if (!value || value->type != GRECS_TYPE_ARRAY || value->v.arg.c != 2) { grecs_error(locus, 0, "expected two strings"); return 1; } if (value->v.arg.v[0]->type != GRECS_TYPE_STRING || value->v.arg.v[1]->type != GRECS_TYPE_STRING) { grecs_error(locus, 0, "expected two strings"); return 1; } define_format(value->v.arg.v[0]->v.string, value->v.arg.v[1]->v.string, &value->v.arg.v[1]->locus); return 0; } static int cb_confirm(enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { struct grecs_list_entry *ep; grecs_value_t *argval; enum eclat_confirm_mode cfmode; char *s; if (cmd != grecs_callback_set_value) { grecs_error(locus, 0, "Unexpected block statement"); return 1; } if (!value || value->type != GRECS_TYPE_ARRAY || value->v.arg.c != 2) { grecs_error(locus, 0, "expected two values"); return 1; } if (value->v.arg.v[0]->type != GRECS_TYPE_STRING) { grecs_error(locus, 0, "first argument not a string"); return 1; } s = value->v.arg.v[0]->v.string; if (strcmp(s, "positive") == 0) cfmode = eclat_confirm_positive; else if (strcmp(s, "negative") == 0) cfmode = eclat_confirm_negative; else if (strcmp(s, "tty") == 0) cfmode = eclat_confirm_tty; else if (strcmp(s, "always") == 0) cfmode = eclat_confirm_always; else { grecs_error(&value->v.arg.v[0]->locus, 0, "unrecognized confirmation mode"); return 1; } switch (value->v.arg.v[1]->type) { case GRECS_TYPE_STRING: set_command_confirmation(value->v.arg.v[1]->v.string, cfmode, &value->v.arg.v[1]->locus); return 0; case GRECS_TYPE_LIST: break; default: grecs_error(locus, 0, "second argument not a list"); return 1; } for (ep = value->v.arg.v[1]->v.list->head; ep; ep = ep->next) { argval = ep->data; if (argval->type != GRECS_TYPE_STRING) { grecs_error(&argval->locus, 0, "list element not a string"); continue; } set_command_confirmation(argval->v.string, cfmode, &argval->locus); } return 0; } static struct grecs_keyword ssl_kw[] = { { "enable", NULL, "Use SSL", grecs_type_bool, GRECS_DFLT, &use_ssl }, { "verify", NULL, "Verify peer certificate", grecs_type_bool, GRECS_DFLT, &ssl_verify_peer }, { "ca-file", NULL, "File holding CA certificates", grecs_type_string, GRECS_DFLT, &ssl_ca_file }, { "ca-path", NULL, "Directory holding files with CA certificates", grecs_type_string, GRECS_DFLT, &ssl_ca_path }, { NULL } }; static int cb_ssl(enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { if (cmd == grecs_callback_set_value) { if (!value || value->type != GRECS_TYPE_STRING || grecs_string_convert(&use_ssl, grecs_type_bool, value->v.string, &value->locus)) { grecs_error (value ? &value->locus : locus, 0, _("expected boolean value")); return 1; } } return 0; } static int cb_authentication_provider(enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { char *type; char *arg = NULL; if (cmd != grecs_callback_set_value) { grecs_error(locus, 0, "Unexpected block statement"); return 1; } if (!value) { grecs_error(locus, 0, "expected one to two values"); return 1; } if (value->type == GRECS_TYPE_ARRAY) { if (value->v.arg.c != 2) { grecs_error(locus, 0, "expected one to two values"); return 1; } if (value->v.arg.v[0]->type != GRECS_TYPE_STRING) { grecs_error(&value->v.arg.v[0]->locus, 0, "first argument not a string"); return 1; } if (value->v.arg.v[1]->type != GRECS_TYPE_STRING) { grecs_error(&value->v.arg.v[1]->locus, 0, "second argument not a string"); return 1; } type = value->v.arg.v[0]->v.string; arg = value->v.arg.v[1]->v.string; } else if (value->type == GRECS_TYPE_STRING) { type = value->v.string; arg = NULL; } if (strcmp(type, "file") == 0) { if (!arg) { grecs_error(locus, 0, "required argument missing"); return 1; } authentication_provider = authp_file; free(access_file_name); access_file_name = grecs_strdup(value->v.arg.v[1]->v.string); } else if (strcmp(type, "instance-store") == 0) { authentication_provider = authp_instance; free(access_key); if (arg) access_key = grecs_strdup(arg); else access_key = NULL; } else { grecs_error(&value->locus, 0, "unknown provider"); } return 0; } static int cb_access_file(enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { if (cmd != grecs_callback_set_value) { grecs_error(locus, 0, "Unexpected block statement"); return 1; } if (!value || value->type != GRECS_TYPE_STRING) { grecs_error(locus, 0, "expected string value"); return 1; } authentication_provider = authp_file; free(*(char**)varptr); *(char**)varptr = grecs_strdup(value->v.string); return 0; } static int cb_http_method(enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { if (cmd != grecs_callback_set_value) { grecs_error(locus, 0, "Unexpected block statement"); return 1; } if (!value || value->type != GRECS_TYPE_STRING) { grecs_error(locus, 0, "expected string value"); return 1; } if (strcasecmp(value->v.string, "post") == 0) use_post = 1; else if (strcasecmp(value->v.string, "get")) grecs_error(&value->locus, 0, "invalid http method"); return 0; } static struct grecs_keyword instance_store_kw[] = { { "base-url", "URL", "Base URL of the instance store", grecs_type_string, GRECS_DFLT, &instance_store_base_url }, { "port", "NUMBER", "Port number", grecs_type_ushort, GRECS_DFLT, &instance_store_port }, { "document-path", "PATH", "Path to the instance identity document file", grecs_type_ushort, GRECS_DFLT, &instance_store_document_path }, { "credentials-path", "PATH", "Path to the instance store credentials directory", grecs_type_ushort, GRECS_DFLT, &instance_store_credentials_path }, { NULL } }; static struct grecs_keyword eclat_kw[] = { { "default-endpoint", "hostname", "Set default EC2 endpoint", grecs_type_string, GRECS_CONST, &endpoint }, { "region", " ", "Define a region", grecs_type_string, GRECS_MULT, NULL, 0, cb_region }, { "signature-version", "version", "Signature version", grecs_type_string, GRECS_CONST, &signature_version }, { "authentication-provider", " ", "Define authentication provider.", grecs_type_string, GRECS_DFLT, NULL, 0, cb_authentication_provider }, { "access-file", "name", "A shorthand for\n" " authentication-provider file \n" "Specifies a file containing `accessID:accessKey' pairs.", grecs_type_string, GRECS_DFLT, &access_file_name, 0, cb_access_file }, { "max-retry-interval", "seconds", "Maximum interval between retries in exponential backoff algorithm.", grecs_type_ulong, GRECS_DFLT, &max_retry_sleep }, { "total-retry-timeout", "seconds", "Give up retrying after this many seconds", grecs_type_ulong, GRECS_DFLT, &max_retry_time }, { "default-region", "name", "Define default AWS region", grecs_type_string, GRECS_DFLT, ®ion_name }, { "ssl", NULL, "Configure SSL. The ssl keyword can also be used in scalar form, like this:\n\n" " ssl yes;\n" "\n" "Use this form if you don't need any fine tuning and only wish to enable\n" "or disable SSL.\n\n" "By default SSL is disabled." , grecs_type_section, GRECS_DFLT, NULL, 0, cb_ssl, NULL, ssl_kw }, { "http-method", "arg: post|get", "Define HTTP method to use", grecs_type_string, GRECS_DFLT, NULL, 0, cb_http_method }, { "format", " ", "Set default format for ", grecs_type_string, GRECS_MULT, NULL, 0, cb_format }, { "define-format", " ", "Define a named format", grecs_type_string, GRECS_MULT, NULL, 0, cb_define_format }, { "format-file", "file", "Read format from ", grecs_type_string, GRECS_DFLT, &format_file }, { "map", "name: string", "Configure a map", grecs_type_section, GRECS_INAC }, { "confirm", " ", "Set confirmation mode", grecs_type_string, GRECS_LIST, NULL, 0, cb_confirm }, { "translate", NULL, "Use ID translation by default", grecs_type_bool, GRECS_DFLT, &translation_enabled }, { "instance-store", NULL, "Configure instance store parameters", grecs_type_section, GRECS_DFLT, NULL, 0, NULL, NULL, instance_store_kw }, { NULL } }; void config_help() { static char docstring[] = N_("Configuration file structure for eclat.\n" "For more information, use `info eclat configuration'."); grecs_print_docstring(docstring, 0, stdout); grecs_print_statement_array(eclat_kw, 1, 0, stdout); eclat_map_confhelp(); } static void grecs_print_diag(grecs_locus_t const *locus, int err, int errcode, const char *msg) { diag(locus, err ? NULL : "warning", "%s", msg); } void config_init() { grecs_include_path_setup(DEFAULT_VERSION_INCLUDE_DIR, DEFAULT_INCLUDE_DIR, NULL); grecs_preprocessor = DEFAULT_PREPROCESSOR; grecs_log_to_stderr = 1; grecs_parser_options = GRECS_OPTION_ADJUST_STRING_LOCATIONS | GRECS_OPTION_QUOTED_STRING_CONCAT; grecs_print_diag_fun = grecs_print_diag; ec2_regtab = grecs_symtab_create_default(sizeof(struct ec2_param)); } void config_finish(struct grecs_node *tree) { struct grecs_node *node; for (node = tree; node; node = node->next) { struct eclat_map *map; node = grecs_find_node(node, "map"); if (!node) break; eclat_map_config(node, &map); } grecs_tree_reduce(tree, eclat_kw, GRECS_AGGR); if (debug_level(ECLAT_DEBCAT_CONF)) { grecs_print_node(tree, GRECS_NODE_FLAG_DEFAULT, stderr); fputc('\n', stdout); } if (grecs_tree_process(tree, eclat_kw)) exit(EX_CONFIG); if (grecs_error_count || run_config_finish_hooks()) exit(EX_CONFIG); if (use_post) signature_version = "4"; }