aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2012-09-20 00:49:00 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2012-09-20 00:49:00 +0300
commitdf0d5f4df874bd47e5e47d08c67fea7ac17eec1e (patch)
tree2097591c3fd1a8e82bf6101c6d311fc9480917f1
parent96871660d41c0130460f52057c0334cec46f7dd3 (diff)
downloadeclat-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.am4
-rw-r--r--lib/libeclat.h12
-rw-r--r--lib/q2url.c16
-rw-r--r--lib/qaddparm.c33
-rw-r--r--lib/qcreat.c45
-rw-r--r--lib/qencode.c39
-rw-r--r--lib/qfree.c29
-rw-r--r--lib/reqsign.c36
-rw-r--r--src/config.c2
-rw-r--r--src/eclat.c110
-rw-r--r--src/eclat.h4
-rw-r--r--src/startinst.c42
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

Return to:

Send suggestions and report system problems to the System administrator.