/* This file is part of Eclat.
Copyright (C) 2012-2015 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_DFLT, &endpoint },
{ "region", " ",
"Define a region",
grecs_type_string, GRECS_MULT, NULL, 0, cb_region },
{ "signature-version", "version",
"Signature version",
grecs_type_string, GRECS_DFLT, &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 },
{ "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_adjust_string_locations = 1;
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";
}