diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2012-09-20 00:49:00 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2012-09-20 00:49:00 +0300 |
commit | df0d5f4df874bd47e5e47d08c67fea7ac17eec1e (patch) | |
tree | 2097591c3fd1a8e82bf6101c6d311fc9480917f1 | |
parent | 96871660d41c0130460f52057c0334cec46f7dd3 (diff) | |
download | eclat-df0d5f4df874bd47e5e47d08c67fea7ac17eec1e.tar.gz eclat-df0d5f4df874bd47e5e47d08c67fea7ac17eec1e.tar.bz2 |
Implement start-instance.
* lib/qaddparm.c: New file.
* lib/qcreat.c: New file.
* lib/qencode.c: New file.
* lib/qfree.c: new file.
* lib/Makefile.am: Add new files.
* lib/libeclat.h (ec2_query) <https, verb>: Remove.
<flags>: New member.
(eclat_query_create, eclat_query_free)
(eclat_query_encode): New protos.
* lib/q2url.c (eclat_query_to_url): Fix.
* lib/reqsign.c (eclat_query_signature): Bugfixes.
* src/config.c (config_finish): Call grecs_tree_process.
* src/eclat.c (url_base): Remove.
(main): Install curl debugging function if required.
* src/eclat.h: Update.
* src/startinst.c (eclat_start_instance): Implement.
-rw-r--r-- | lib/Makefile.am | 4 | ||||
-rw-r--r-- | lib/libeclat.h | 12 | ||||
-rw-r--r-- | lib/q2url.c | 16 | ||||
-rw-r--r-- | lib/qaddparm.c | 33 | ||||
-rw-r--r-- | lib/qcreat.c | 45 | ||||
-rw-r--r-- | lib/qencode.c | 39 | ||||
-rw-r--r-- | lib/qfree.c | 29 | ||||
-rw-r--r-- | lib/reqsign.c | 36 | ||||
-rw-r--r-- | src/config.c | 2 | ||||
-rw-r--r-- | src/eclat.c | 110 | ||||
-rw-r--r-- | src/eclat.h | 4 | ||||
-rw-r--r-- | src/startinst.c | 42 |
12 files changed, 343 insertions, 29 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index eeb3dcb..7843433 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -21,6 +21,10 @@ libeclat_a_SOURCES=\ hmac_sha1.c\ libeclat.h\ q2url.c\ + qaddparm.c\ + qcreat.c\ + qencode.c\ + qfree.c\ reqsign.c\ sha1.c\ sha1.h\ diff --git a/lib/libeclat.h b/lib/libeclat.h index aacc0e2..e2dfc44 100644 --- a/lib/libeclat.h +++ b/lib/libeclat.h @@ -33,17 +33,25 @@ struct ec2_param { char *value; }; +#define EC2_QF_HTTPS 0x01 +#define EC2_QF_POST 0x02 + struct ec2_query { - int https; + int flags; /* Composed from EC2_QF_* bits */ char *endpoint; /* endpoint */ char *uri; /* URI without parameters */ - char *verb; /* GET or POST */ struct grecs_symtab *params; /* Query parameters */ char *signature; + unsigned long ttl; /* Time-to-live in seconds */ }; +struct ec2_query *eclat_query_create(int flags, const char *endpoint, + const char *uri); +void eclat_query_free(struct ec2_query *); + int eclat_query_signature(struct ec2_query *req, char *secret); char *eclat_query_to_url(struct ec2_query *req, char **post_params); +void eclat_query_encode(struct ec2_query *q); diff --git a/lib/q2url.c b/lib/q2url.c index a96e2e9..d677404 100644 --- a/lib/q2url.c +++ b/lib/q2url.c @@ -37,18 +37,18 @@ char * eclat_query_to_url(struct ec2_query *req, char **post_params) { struct grecs_txtacc *acc; - char *ret = NULL; + char *ret = NULL, *p; acc = grecs_txtacc_create(); grecs_txtacc_grow(acc, "http", 4); - if (req->https) + if (req->flags & EC2_QF_HTTPS) grecs_txtacc_grow_char(acc, 's'); - grecs_txtacc_grow(acc, "//", 2); + grecs_txtacc_grow(acc, "://", 3); grecs_txtacc_grow(acc, req->endpoint, strlen(req->endpoint)); grecs_txtacc_grow(acc, req->uri, strlen(req->uri)); - if (strcmp(req->verb, "POST") == 0) { + if (req->flags & EC2_QF_POST) { grecs_txtacc_grow_char(acc, 0); ret = grecs_txtacc_finish(acc, 1); } else { @@ -56,9 +56,13 @@ eclat_query_to_url(struct ec2_query *req, char **post_params) } /* Add signature */ - grecs_txtacc_grow(acc, "Signature", sizeof ("Signature")); + grecs_txtacc_grow(acc, "Signature", sizeof("Signature")-1); grecs_txtacc_grow_char(acc, '='); - grecs_txtacc_grow(acc, req->signature, strlen(req->signature)); + + urlencode(req->signature, strlen(req->signature), &p, NULL); + grecs_txtacc_grow(acc, p, strlen(p)); + free(p); + /* Add other parameters */ grecs_symtab_enumerate(req->params, add_param, acc); diff --git a/lib/qaddparm.c b/lib/qaddparm.c new file mode 100644 index 0000000..0378eaa --- /dev/null +++ b/lib/qaddparm.c @@ -0,0 +1,33 @@ +/* This file is part of Eclat. + Copyright (C) 2012 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 <config.h> +#include <string.h> +#include "libeclat.h" +#include "grecs.h" + +void +eclat_query_add_param(struct ec2_query *q, const char *name, const char *value) +{ + struct ec2_param *p, key; + int install = 1; + + key.name = (char*) name; + p = grecs_symtab_lookup_or_install(q->params, &key, &install); + if (!install) + free(p->value); + p->value = grecs_strdup(value); +} diff --git a/lib/qcreat.c b/lib/qcreat.c new file mode 100644 index 0000000..a25f528 --- /dev/null +++ b/lib/qcreat.c @@ -0,0 +1,45 @@ +/* This file is part of Eclat. + Copyright (C) 2012 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 <config.h> +#include "libeclat.h" +#include "grecs.h" + +static void +ec2_param_free(void *ptr) +{ + struct ec2_param *p = ptr; + free(p->name); + free(p->value); + free(p); +} + +struct ec2_query * +eclat_query_create(int flags, const char *endpoint, const char *uri) +{ + struct ec2_query *q = grecs_zalloc(sizeof(*q)); + q->flags = flags; + q->params = grecs_symtab_create(sizeof(struct ec2_param), + NULL, + NULL, + NULL, + NULL, + ec2_param_free); + q->endpoint = grecs_strdup(endpoint); + q->uri = grecs_strdup(uri); + q->ttl = 5; + return q; +} diff --git a/lib/qencode.c b/lib/qencode.c new file mode 100644 index 0000000..5c11a1a --- /dev/null +++ b/lib/qencode.c @@ -0,0 +1,39 @@ +/* This file is part of Eclat. + Copyright (C) 2012 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 <config.h> +#include <string.h> +#include "libeclat.h" +#include "grecs.h" + +static int +encode_param(void *sym, void *data) +{ + struct ec2_param *p = sym; + char *enc; + + urlencode(p->value, strlen(p->value), &enc, NULL); + free(p->value); + p->value = enc; + return 0; +} + +void +eclat_query_encode(struct ec2_query *q) +{ + grecs_symtab_enumerate(q->params, encode_param, NULL); +} + diff --git a/lib/qfree.c b/lib/qfree.c new file mode 100644 index 0000000..37a7c8e --- /dev/null +++ b/lib/qfree.c @@ -0,0 +1,29 @@ +/* This file is part of Eclat. + Copyright (C) 2012 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 <config.h> +#include "libeclat.h" +#include "grecs.h" + +void +eclat_query_free(struct ec2_query *q) +{ + free(q->endpoint); + free(q->uri); + grecs_symtab_free(q->params); + free(q); +} + diff --git a/lib/reqsign.c b/lib/reqsign.c index f8ad234..20c56a7 100644 --- a/lib/reqsign.c +++ b/lib/reqsign.c @@ -16,6 +16,7 @@ #include <config.h> #include <string.h> +#include <time.h> #include "libeclat.h" #include "grecs.h" @@ -51,9 +52,22 @@ eclat_query_signature(struct ec2_query *req, char *secret) char *str; char digest[20]; size_t siglen; + const char *verb; + char tsbuf[22]; + time_t t; acc = grecs_txtacc_create(); - + + /* Add default parameters */ + eclat_query_add_param(req, "SignatureMethod", "HmacSHA1"); + eclat_query_add_param(req, "SignatureVersion", "2"); + + time(&t); + strftime(tsbuf, sizeof(tsbuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t)); + eclat_query_add_param(req, "Timestamp", tsbuf); + + eclat_query_encode(req); + /* Collect and sort parameter names */ n = grecs_symtab_count_entries(req->params); pnames = grecs_calloc(n, sizeof(pnames[0])); @@ -62,7 +76,8 @@ eclat_query_signature(struct ec2_query *req, char *secret) grecs_symtab_enumerate(req->params, get_param_name, &pn); qsort(pnames, n, sizeof(pnames[0]), compnames); - grecs_txtacc_grow(acc, req->verb, strlen(req->verb)); + verb = (req->flags & EC2_QF_POST) ? "POST" : "GET"; + grecs_txtacc_grow(acc, verb, strlen(verb)); grecs_txtacc_grow_char(acc, '\n'); grecs_txtacc_grow(acc, req->endpoint, strlen(req->endpoint)); grecs_txtacc_grow_char(acc, '\n'); @@ -71,9 +86,10 @@ eclat_query_signature(struct ec2_query *req, char *secret) /* Append a canonicalized query string */ for (i = 0; i < n; i++) { - struct ec2_param *p = - grecs_symtab_lookup_or_install(req->params, pnames[i], - NULL); + struct ec2_param *p, key; + + key.name = pnames[i]; + p = grecs_symtab_lookup_or_install(req->params, &key, NULL); if (!p) abort(); if (i != 0) @@ -89,8 +105,14 @@ eclat_query_signature(struct ec2_query *req, char *secret) hmac_sha1(str, strlen(str), secret, strlen(secret), digest); eclat_base64_encode(digest, sizeof(digest), - (unsigned char**) req->signature, &siglen); - + (unsigned char**) &req->signature, &siglen); + grecs_txtacc_free(acc); free(pnames); + +/*FIXME + t += req->ttl; + strftime(tsbuf, sizeof(tsbuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t)); + eclat_query_add_param(req, "Expires", tsbuf); +*/ } diff --git a/src/config.c b/src/config.c index 184b42a..5727167 100644 --- a/src/config.c +++ b/src/config.c @@ -102,6 +102,8 @@ config_finish(struct grecs_node *tree) 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); } diff --git a/src/eclat.c b/src/eclat.c index 3d2572d..5625076 100644 --- a/src/eclat.c +++ b/src/eclat.c @@ -29,8 +29,6 @@ char *secret_key; char *region_name; enum eclat_command eclat_command; -char *url_base; - struct debug_trans { const char *name; @@ -44,6 +42,7 @@ static struct debug_trans debug_trans[] = { { S(cfgram), ECLAT_DEBCAT_CFGRAM }, { S(cflex), ECLAT_DEBCAT_CFLEX }, { S(conf), ECLAT_DEBCAT_CONF }, + { S(curl), ECLAT_DEBCAT_CURL }, { NULL } }; @@ -85,6 +84,94 @@ parse_debug_level(const char *arg) return 0; } +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; +} + + + #include "cmdline.h" eclat_command_handler_t handler_tab[] = { @@ -99,7 +186,6 @@ main(int argc, char **argv) int index, rc; struct grecs_node *tree; CURL *curl; - size_t size; set_program_name(argv[0]); config_init(); @@ -143,19 +229,19 @@ main(int argc, char **argv) } if (eclat_command == eclat_command_unspecified) - die(EX_USAGE, "no command given"); - - url_base = NULL; - size = 0; - if (use_ssl) - grecs_asprintf(&url_base, &size, "https://%s", default_host); - else - grecs_asprintf(&url_base, &size, "http://%s", default_host); - + die(EX_USAGE, "no command given"); 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); + } + rc = handler_tab[eclat_command](curl, argc, argv); curl_easy_cleanup(curl); exit(rc); diff --git a/src/eclat.h b/src/eclat.h index 0fb4fd7..953c34c 100644 --- a/src/eclat.h +++ b/src/eclat.h @@ -31,7 +31,8 @@ #define ECLAT_DEBCAT_CFGRAM 1 #define ECLAT_DEBCAT_CFLEX 2 #define ECLAT_DEBCAT_CONF 3 -#define ECLAT_DEBCAT_MAX 4 +#define ECLAT_DEBCAT_CURL 4 +#define ECLAT_DEBCAT_MAX 5 extern const char *program_name; extern int debug_level[]; @@ -42,7 +43,6 @@ extern char *region_name; extern char *access_file_name; extern char *access_key; extern char *secret_key; -extern char *url_base; #define debug(cat, lev, s) \ do { \ diff --git a/src/startinst.c b/src/startinst.c index f07e50a..87887e9 100644 --- a/src/startinst.c +++ b/src/startinst.c @@ -16,9 +16,51 @@ #include "eclat.h" +#define EC2_API_VERSION "2012-08-15" + int eclat_start_instance(CURL *curl, int argc, char **argv) { + int i; + struct ec2_query *q; + char buf[128], *bend, *url; + size_t bs; + CURLcode res; + + q = eclat_query_create(use_ssl ? EC2_QF_HTTPS : 0, + default_host, "/"); + if (region_name) + eclat_query_add_param(q, "Placement.AvailabilityZone", + region_name); + eclat_query_add_param(q, "Action", "StartInstances"); + + strcpy(buf, "InstanceId."); + bend = buf + strlen(buf); + bs = sizeof(buf) - strlen(buf); + for (i = 0; i < argc; i++) { + snprintf(bend, bs, "%lu", (unsigned long)(i + 1)); + eclat_query_add_param(q, buf, argv[i]); + } + + eclat_query_add_param(q, "AWSAccessKeyId", access_key); + eclat_query_add_param(q, "Version", EC2_API_VERSION); + + eclat_query_signature(q, secret_key); + url = eclat_query_to_url(q, NULL); + + debug(ECLAT_DEBCAT_MAIN, 2, ("using URL: %s", url)); + curl_easy_setopt(curl, CURLOPT_URL, url); + + free(url); + eclat_query_free(q); + + res = curl_easy_perform(curl); + + if (res != CURLE_OK) + err("CURL: %s", curl_easy_strerror(res)); + + return 0; + } int |