diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-05-04 23:01:13 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-05-04 23:09:30 +0300 |
commit | 9ead52193e7edb563ec58ef1a15ee9291b418c8e (patch) | |
tree | 4990815b2bc4fc93c2d0ef72d1038ffaf32f9a83 | |
parent | e59f4da93d617eab8a5c0f85681aed1351011a03 (diff) | |
download | eclat-9ead52193e7edb563ec58ef1a15ee9291b418c8e.tar.gz eclat-9ead52193e7edb563ec58ef1a15ee9291b418c8e.tar.bz2 |
Implement EC2 map.
EC2 map allows to use AWS services as eclap maps. For example,
one may use an EC2 tag to map resource names to resource IDs, etc.
* src/ec2map.c: New file.
* src/io.c: New file.
* src/Makefile.am (eclat_SOURCES): Add new files.
* src/eclat.c (dump,eclat_trace_fun)
(dumpxml,write_callback): Move to io.c
(main): Register ec2 map.
Use eclat_io framework.
* src/eclat.h (eclat_io): New struct.
(eclat_io_init, eclat_io_free)
(eclat_io_shutdown, eclat_io_finish): New functions.
(eclat_map_drv_ec2): New extern.
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/ec2map.c | 230 | ||||
-rw-r--r-- | src/eclat.c | 191 | ||||
-rw-r--r-- | src/eclat.h | 13 | ||||
-rw-r--r-- | src/io.c | 255 |
5 files changed, 510 insertions, 181 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 6b796b1..a402157 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -28,10 +28,12 @@ eclat_SOURCES=\ deimg.c\ devol.c\ disasaddr.c\ + ec2map.c\ eclat.c\ eclat.h\ genericcl.c\ getconout.c\ + io.c\ lsaddr.c\ lsattr.c\ lsiattr.c\ diff --git a/src/ec2map.c b/src/ec2map.c new file mode 100644 index 0000000..6eb7e8b --- /dev/null +++ b/src/ec2map.c @@ -0,0 +1,230 @@ +/* This file is part of Eclat. + Copyright (C) 2012, 2013 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 <http://www.gnu.org/licenses/>. */ + +#include "eclat.h" +#include <errno.h> + +/* + map "SnapshotId" { + type ec2; + action DescribeImages; + arguments ("Owner.1=self", + "Filter.1.Name=tag:Name", + "Filter.1.Value.1=${key}); + return ".DescribeImagesResponse.imagesSet.item.imageId"; + } +*/ + +struct ec2_map { + char *action; + struct grecs_list *args; + char *ret; + struct eclat_io *io; +}; + +static struct grecs_keyword ec2_map_kw[] = { + { "type", "'ec2", "Set map type", + grecs_type_null }, + { "key", "<arg: string>", "key expression", + grecs_type_null }, + { "action", NULL, "EC2 action", + grecs_type_string, GRECS_DFLT, NULL, + offsetof(struct ec2_map, action) }, + { "return", NULL, "return element", + grecs_type_string, GRECS_DFLT, NULL, + offsetof(struct ec2_map, ret) }, + { "arguments", NULL, "EC2 action", + grecs_type_string, GRECS_LIST, NULL, + offsetof(struct ec2_map, args) }, + { NULL } +}; + +static void +ec2_map_free(int dbg, void *data) +{ + struct ec2_map *map = data; + eclat_io_free(map->io); + free(map->action); + free(map->ret); + grecs_list_free(map->args); + free(map); +} + +static void +ec2_map_confhelp() +{ + static struct grecs_keyword ec2_map_top[] = { + { "map", "name: string", + "Configuration for a EC2 map", + grecs_type_section, GRECS_INAC, NULL, 0, NULL, NULL, + ec2_map_kw }, + { NULL } + }; + grecs_print_statement_array(ec2_map_top, 1, 0, stdout); +} + +static int +ec2_map_config(int dbg, struct grecs_node *node, void *data) +{ + int i; + int ec = 0; + struct ec2_map *map, **return_map = data; + + map = grecs_zalloc(sizeof(*map)); + for (i = 0; ec2_map_kw[i].ident; i++) + ec2_map_kw[i].varptr = map; + if (grecs_tree_process(node->down, ec2_map_kw)) { + ec2_map_free(dbg, map); + return eclat_map_failure; + } + if (!map->action) { + grecs_error(&node->locus, 0, "no action statement"); + ec++; + } + if (!map->ret) { + grecs_error(&node->locus, 0, "no return statement"); + ec++; + } + if (!map->args) { + grecs_error(&node->locus, 0, "no arguments statement"); + ec++; + } + if (ec) { + ec2_map_free(dbg, map); + return eclat_map_failure; + } + + *return_map = map; + return eclat_map_ok; +} + +static int +ec2_map_open(int dbg, void *data) +{ + struct ec2_map *map = data; + + map->io = eclat_io_init(0); + if (!map->io) { + err("cannot open EC2 database"); + return eclat_map_failure; + } + return eclat_map_ok; +} + +static int +ec2_map_close(int dbg, void *data) +{ + struct ec2_map *map = data; + + eclat_io_free(map->io); + map->io = NULL; + return 0; +} + +static int +ec2_map_get(int dbg, int dir, void *data, const char *key, char **return_value) +{ + struct ec2_map *map = data; + struct ec2_query *q; + struct grecs_list_entry *ep; + struct wordsplit ws; + int wsflags; + char *env[3]; + struct grecs_node *tree, *node; + int rc; + + q = eclat_query_create(use_ssl ? EC2_QF_HTTPS : 0, endpoint, "/"); + eclat_query_add_param(q, "Action", map->action); + + env[0] = "key"; + env[1] = key; + env[2] = NULL; + + ws.ws_env = env; + ws.ws_error = err; + wsflags = WRDSF_NOSPLIT | WRDSF_NOCMD | + WRDSF_ENV | WRDSF_ENV_KV | WRDSF_WARNUNDEF | WRDSF_ERROR; + + rc = 0; + for (ep = map->args->head; ep; ep = ep->next) { + char *p; + + if (wordsplit((char *)ep->data, &ws, wsflags)) { + rc = 1; + break; + } + wsflags |= WRDSF_REUSE; + p = strchr(ws.ws_wordv[0], '='); + if (p) { + *p++ = 0; + eclat_query_add_param(q, ws.ws_wordv[0], p); + } else + eclat_query_add_param(q, ws.ws_wordv[0], ""); + } + + if (wsflags & WRDSF_REUSE) + wordsplit_free(&ws); + + if (rc) { + eclat_query_free(q); + return eclat_map_failure; + } + + rc = eclat_send_query(map->io->curl, q); + + if (rc) + return eclat_map_failure; + + tree = eclat_io_finish(map->io); + + + node = grecs_find_node(tree, map->ret); + + if (debug_category[dbg].level > 1) { + debug(dbg, 2, ("Got node:")); + grecs_print_node(node, GRECS_NODE_FLAG_DEFAULT, stderr); + fputc('\n', stderr); + } + + rc = eclat_map_not_found; + if (node) { + if (node->type == grecs_node_stmt && + node->v.value->type == GRECS_TYPE_STRING) { + *return_value = grecs_strdup(node->v.value->v.string); + rc = eclat_map_ok; + } else { + rc = eclat_map_failure; + } + } + + grecs_tree_free(tree); + + return rc; +} + +struct eclat_map_drv eclat_map_drv_ec2 = { + "ec2", + ec2_map_config, + ec2_map_open, + ec2_map_close, + ec2_map_get, + ec2_map_free, + ec2_map_confhelp +}; + + + + diff --git a/src/eclat.c b/src/eclat.c index c00c795..7048fe5 100644 --- a/src/eclat.c +++ b/src/eclat.c @@ -60,137 +60,6 @@ debug_init() } -static void -dump(const char *text, FILE *stream, unsigned char *ptr, size_t size) -{ - size_t i; - size_t c; - unsigned int width = 0x10; - int hex = debug_level(ECLAT_DEBCAT_CURL) > 2; - - if (!hex) - /* without the hex output, we can fit more on screen */ - width = 0x40; - - fprintf(stream, "%s, %zd bytes (0x%zx)\n", text, size, size); - - for (i = 0; i < size; i += width) { - fprintf(stream, "%04zx: ", i); - - if (hex) { - for (c = 0; c < width; c++) - if (i+c < size) - fprintf(stream, "%02x ", ptr[i+c]); - else - fputs(" ", stream); - } - - for(c = 0; (c < width) && (i+c < size); c++) { - /* check for CRLf; if found, skip past and start a - new line of output */ - if (!hex && (i + c + 1 < size) && - ptr[i+c] == '\r' && ptr[i+c+1] == '\n') { - i += (c + 2 -width); - break; - } - fprintf(stream, "%c", - isprint(ptr[i+c]) ? ptr[i+c] : '.'); - /* check again for CRLF, to avoid an extra \n if - it's at width */ - if (!hex && (i + c + 2 < size) && - ptr[i+c+1] == '\r' && ptr[i+c+2] == '\n') { - i += (c + 3 - width); - break; - } - } - fputc('\n', stream); /* newline */ - } - fflush(stream); -} - -static int -eclat_trace_fun(CURL *handle, curl_infotype type, - char *data, size_t size, - void *userp) -{ -/* struct data *config = (struct data *)userp;*/ - const char *text; - - switch (type) { - case CURLINFO_TEXT: - fprintf(stderr, "== Info: %s", data); - default: /* in case a new one is introduced to shock us */ - return 0; - - case CURLINFO_HEADER_OUT: - text = "=> Send header"; - break; - case CURLINFO_DATA_OUT: - text = "=> Send data"; - break; - case CURLINFO_SSL_DATA_OUT: - text = "=> Send SSL data"; - break; - case CURLINFO_HEADER_IN: - text = "<= Recv header"; - break; - case CURLINFO_DATA_IN: - text = "<= Recv data"; - break; - case CURLINFO_SSL_DATA_IN: - text = "<= Recv SSL data"; - break; - } - - dump(text, stderr, (unsigned char *)data, size); - return 0; -} - -static void -dumpxml(void *ptr, size_t realsize) -{ - static int open_failed = 0; - - if (open_failed) - return; - if (!xml_dump_file) { - xml_dump_file = fopen(XML_DUMP_FILE_NAME, "w"); - if (!xml_dump_file) { - err("cannot open dump file %s: %s", - XML_DUMP_FILE_NAME, strerror(errno)); - open_failed = 1; - return; - } - } - fwrite(ptr, realsize, 1, xml_dump_file); -} - -static size_t -write_callback(void *ptr, size_t size, size_t nmemb, void *data) -{ - size_t realsize = size * nmemb; - XML_Parser parser = data; - enum XML_Status status; - int line = XML_GetCurrentLineNumber(parser); - int column = XML_GetCurrentColumnNumber(parser); - - if (debug_level(ECLAT_DEBCAT_MAIN) >= 10) - dumpxml(ptr, realsize); - - status = XML_Parse(parser, ptr, realsize, 0); - if (status == XML_STATUS_ERROR) { - enum XML_Error error = XML_GetErrorCode(parser); - - line = XML_GetCurrentLineNumber(parser); - column = XML_GetCurrentColumnNumber(parser); - - /* FIXME: better diagnostics. */ - die(EX_SOFTWARE, "XML parse error at %d:%d: %s", - line, column, XML_ErrorString(error)); - } - return realsize; -} - static int node_ident_cmp(struct grecs_node const *a, struct grecs_node const *b) { @@ -492,8 +361,8 @@ print_matching_commands(const char *pat) size_t patlen = strlen (pat); for (cp = cmdtab; cp < cmdtab + cmdcnt; cp++) { - if (cp->name && strlen (cp->name) >= patlen && - memcmp (cp->name, pat, patlen) == 0) + if (cp->name && strlen(cp->name) >= patlen && + memcmp(cp->name, pat, patlen) == 0) printf("%s\n", cp->name); if (cp->ident && ident_matches(cp->ident, pat) != NO_MATCH) printf("%s\n", cp->ident); @@ -785,9 +654,7 @@ main(int argc, char **argv) { int rc; struct grecs_node *tree; - CURL *curl; - XML_Parser parser; - eclat_partial_tree_t part; + struct eclat_io *io; struct grecs_node *xmltree; forlan_eval_env_t env = NULL; struct eclat_command *command = NULL; @@ -808,6 +675,7 @@ main(int argc, char **argv) #ifdef WITH_LDAP eclat_map_drv_register(&eclat_map_drv_ldap); #endif + eclat_map_drv_register(&eclat_map_drv_ec2); sortcmds(); config_init(); parse_options(&argc, &argv); @@ -887,45 +755,8 @@ main(int argc, char **argv) "cannot find authentication credentials"); } debug(ECLAT_DEBCAT_MAIN, 1, ("using access key %s", access_key)); - - curl = curl_easy_init(); - if (!curl) - die(EX_UNAVAILABLE, "curl_easy_init failed"); - - if (debug_level(ECLAT_DEBCAT_CURL)) { - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - if (debug_level(ECLAT_DEBCAT_CURL) > 1) - curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, - eclat_trace_fun); - } - - /* Create XML parser */ - parser = XML_ParserCreate("UTF-8"); - if (!parser) - die(EX_SOFTWARE, "cannot create XML parser"); - - XML_SetElementHandler(parser, - eclat_partial_tree_start_handler, - eclat_partial_tree_end_handler); - XML_SetCharacterDataHandler(parser, - eclat_partial_tree_data_handler); - part = eclat_partial_tree_create(); - XML_SetUserData(parser, part); - - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, parser); - if (use_ssl) { - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, - (long) ssl_verify_peer); - if (ssl_verify_peer) { - if (ssl_ca_file) - curl_easy_setopt(curl, CURLOPT_CAINFO, - ssl_ca_file); - if (ssl_ca_path) - curl_easy_setopt(curl, CURLOPT_CAPATH, - ssl_ca_path); - } - } + + io = eclat_io_init(1); if (confirm_mode == eclat_confirm_unspecified) confirm_mode = command->confirm; @@ -933,19 +764,17 @@ main(int argc, char **argv) /* Prepare environment */ memset(&cmdenv, 0, sizeof(cmdenv)); cmdenv.cmd = command; - cmdenv.curl = curl; - + cmdenv.curl = io->curl; + rc = eclat_do_command(&cmdenv, command, argc, argv); if (rc) exit(rc); - curl_easy_cleanup(curl); - XML_Parse(parser, "", 0, 1); + xmltree = eclat_io_finish(io); + if (xml_dump_file) fclose(xml_dump_file); - xmltree = eclat_partial_tree_finish(part); - if (sort_option) grecs_tree_sort(xmltree, node_ident_cmp); diff --git a/src/eclat.h b/src/eclat.h index 72691ca..3ece7fd 100644 --- a/src/eclat.h +++ b/src/eclat.h @@ -87,7 +87,19 @@ struct eclat_command { struct eclat_command *find_command_name(const char *name); int eclat_do_command(eclat_command_env_t *env, struct eclat_command *command, int argc, char **argv); + +struct eclat_io { + XML_Parser parser; + eclat_partial_tree_t part; + CURL *curl; +}; + +struct eclat_io *eclat_io_init(int errfatal); +void eclat_io_free(struct eclat_io *io); +void eclat_io_shutdown(struct eclat_io *io); +struct grecs_node *eclat_io_finish(struct eclat_io *io); + int eclat_start_instance(eclat_command_env_t *env, int argc, char **argv); int eclat_stop_instance(eclat_command_env_t *env, int argc, char **argv); int eclat_reboot_instance(eclat_command_env_t *env, int argc, char **argv); @@ -196,3 +208,4 @@ extern struct grecs_proginfo *generic_proginfo; void set_command_confirmation(const char *name, enum eclat_confirm_mode cfmode, grecs_locus_t *locus); +extern struct eclat_map_drv eclat_map_drv_ec2; diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..26c3ffe --- /dev/null +++ b/src/io.c @@ -0,0 +1,255 @@ +/* This file is part of Eclat. + Copyright (C) 2012, 2013 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 <http://www.gnu.org/licenses/>. */ + +#include "eclat.h" + +extern FILE *xml_dump_file; + + +static void +dump(const char *text, FILE *stream, unsigned char *ptr, size_t size) +{ + size_t i; + size_t c; + unsigned int width = 0x10; + int hex = debug_level(ECLAT_DEBCAT_CURL) > 2; + + if (!hex) + /* without the hex output, we can fit more on screen */ + width = 0x40; + + fprintf(stream, "%s, %zd bytes (0x%zx)\n", text, size, size); + + for (i = 0; i < size; i += width) { + fprintf(stream, "%04zx: ", i); + + if (hex) { + for (c = 0; c < width; c++) + if (i+c < size) + fprintf(stream, "%02x ", ptr[i+c]); + else + fputs(" ", stream); + } + + for(c = 0; (c < width) && (i+c < size); c++) { + /* check for CRLf; if found, skip past and start a + new line of output */ + if (!hex && (i + c + 1 < size) && + ptr[i+c] == '\r' && ptr[i+c+1] == '\n') { + i += (c + 2 -width); + break; + } + fprintf(stream, "%c", + isprint(ptr[i+c]) ? ptr[i+c] : '.'); + /* check again for CRLF, to avoid an extra \n if + it's at width */ + if (!hex && (i + c + 2 < size) && + ptr[i+c+1] == '\r' && ptr[i+c+2] == '\n') { + i += (c + 3 - width); + break; + } + } + fputc('\n', stream); /* newline */ + } + fflush(stream); +} + +static int +eclat_trace_fun(CURL *handle, curl_infotype type, + char *data, size_t size, + void *userp) +{ +/* struct data *config = (struct data *)userp;*/ + const char *text; + + switch (type) { + case CURLINFO_TEXT: + fprintf(stderr, "== Info: %s", data); + default: /* in case a new one is introduced to shock us */ + return 0; + + case CURLINFO_HEADER_OUT: + text = "=> Send header"; + break; + case CURLINFO_DATA_OUT: + text = "=> Send data"; + break; + case CURLINFO_SSL_DATA_OUT: + text = "=> Send SSL data"; + break; + case CURLINFO_HEADER_IN: + text = "<= Recv header"; + break; + case CURLINFO_DATA_IN: + text = "<= Recv data"; + break; + case CURLINFO_SSL_DATA_IN: + text = "<= Recv SSL data"; + break; + } + + dump(text, stderr, (unsigned char *)data, size); + return 0; +} + +static void +dumpxml(void *ptr, size_t realsize) +{ + static int open_failed = 0; + + if (open_failed) + return; + if (!xml_dump_file) { + xml_dump_file = fopen(XML_DUMP_FILE_NAME, "w"); + if (!xml_dump_file) { + err("cannot open dump file %s: %s", + XML_DUMP_FILE_NAME, strerror(errno)); + open_failed = 1; + return; + } + } + fwrite(ptr, realsize, 1, xml_dump_file); +} + +static size_t +write_callback(void *ptr, size_t size, size_t nmemb, void *data) +{ + size_t realsize = size * nmemb; + XML_Parser parser = data; + enum XML_Status status; + int line = XML_GetCurrentLineNumber(parser); + int column = XML_GetCurrentColumnNumber(parser); + + if (debug_level(ECLAT_DEBCAT_MAIN) >= 10) + dumpxml(ptr, realsize); + + status = XML_Parse(parser, ptr, realsize, 0); + if (status == XML_STATUS_ERROR) { + enum XML_Error error = XML_GetErrorCode(parser); + + line = XML_GetCurrentLineNumber(parser); + column = XML_GetCurrentColumnNumber(parser); + + /* FIXME: better diagnostics. */ + die(EX_SOFTWARE, "XML parse error at %d:%d: %s", + line, column, XML_ErrorString(error)); + } + return realsize; +} + +static int +eclat_io_setup(struct eclat_io *io, int errfatal) +{ + io->curl = curl_easy_init(); + if (!io->curl) { + if (errfatal) + die(EX_UNAVAILABLE, "curl_easy_init failed"); + else { + err("curl_easy_init failed"); + return 1; + } + } + + if (debug_level(ECLAT_DEBCAT_CURL)) { + curl_easy_setopt(io->curl, CURLOPT_VERBOSE, 1L); + if (debug_level(ECLAT_DEBCAT_CURL) > 1) + curl_easy_setopt(io->curl, CURLOPT_DEBUGFUNCTION, + eclat_trace_fun); + } + + /* Create XML parser */ + io->parser = XML_ParserCreate("UTF-8"); + if (!io->parser) { + if (errfatal) + die(EX_SOFTWARE, "cannot create XML parser"); + else { + err("cannot create XML parser"); + return 1; + } + } + + XML_SetElementHandler(io->parser, + eclat_partial_tree_start_handler, + eclat_partial_tree_end_handler); + XML_SetCharacterDataHandler(io->parser, + eclat_partial_tree_data_handler); + io->part = eclat_partial_tree_create(); + XML_SetUserData(io->parser, io->part); + + curl_easy_setopt(io->curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(io->curl, CURLOPT_WRITEDATA, io->parser); + if (use_ssl) { + curl_easy_setopt(io->curl, CURLOPT_SSL_VERIFYPEER, + (long) ssl_verify_peer); + if (ssl_verify_peer) { + if (ssl_ca_file) + curl_easy_setopt(io->curl, CURLOPT_CAINFO, + ssl_ca_file); + if (ssl_ca_path) + curl_easy_setopt(io->curl, CURLOPT_CAPATH, + ssl_ca_path); + } + } + return 0; +} + +void +eclat_io_free(struct eclat_io *io) +{ + if (!io) + return; + eclat_io_shutdown(io); + if (io->part) + eclat_partial_tree_destroy(io->part); + if (io->parser) + XML_ParserFree(io->parser); + free(io); +} + +struct eclat_io * +eclat_io_init(int onerr) +{ + struct eclat_io *io; + + io = grecs_zalloc(sizeof(*io)); + if (eclat_io_setup(io, onerr)) { + eclat_io_free(io); + return NULL; + } + return io; +} + +void +eclat_io_shutdown(struct eclat_io *io) +{ + if (io->curl) { + curl_easy_cleanup(io->curl); + io->curl = NULL; + } +} + +struct grecs_node * +eclat_io_finish(struct eclat_io *io) +{ + struct grecs_node *p; + + eclat_io_shutdown(io); + XML_Parse(io->parser, "", 0, 1); + p = eclat_partial_tree_finish(io->part); + io->part = NULL; + return p; +} + |