aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS16
-rw-r--r--doc/eclat.1man39
-rw-r--r--lib/.gitignore4
-rw-r--r--lib/Makefile.am10
-rw-r--r--lib/json.h66
-rw-r--r--lib/jsongrm.y316
-rw-r--r--lib/jsonlex.l183
-rw-r--r--lib/libeclat.h3
-rw-r--r--lib/qcreat.c4
-rw-r--r--lib/reqsign.c2
-rw-r--r--lib/yytrans4
-rw-r--r--src/accfile.c2
-rw-r--r--src/cmdline.opt4
-rw-r--r--src/ec2map.c2
-rw-r--r--src/eclat.c17
-rw-r--r--src/eclat.h4
-rw-r--r--src/util.c96
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile.am1
-rw-r--r--tests/tjson.c116
20 files changed, 833 insertions, 57 deletions
diff --git a/NEWS b/NEWS
index be9c4a8..aebbc83 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,5 @@
-Eclat NEWS -- history of user-visible changes. 2014-07-09
-Copyright (C) 2012-2014 Sergey Poznyakoff
+Eclat NEWS -- history of user-visible changes. 2015-01-19
+Copyright (C) 2012-2015 Sergey Poznyakoff
See the end of file for copying conditions.
Please send Eclat bug reports to <gray+eclat@gnu.org.ua>
@@ -18,7 +18,15 @@ The signature version 4 signing process is enabled by adding the
following statement in the eclat configuration file:
signature-version 4;
-
+
+* If availability region is not supplied, it is read from the instance store.
+
+* IAM support
+
+If access key is not found in the access file, it is assumed to be a
+IAM role name. The authentication credentials are then taken from the
+instance store.
+
Version 1.0, 2013-12-20
@@ -32,7 +40,7 @@ First alpha release.
=========================================================================
Copyright information:
-Copyright (C) 2012, 2013 Sergey Poznyakoff
+Copyright (C) 2012-2015 Sergey Poznyakoff
Permission is granted to anyone to make or distribute verbatim copies
of this document as received, in any medium, provided that the
diff --git a/doc/eclat.1man b/doc/eclat.1man
index 0d39f21..9560e81 100644
--- a/doc/eclat.1man
+++ b/doc/eclat.1man
@@ -1,5 +1,5 @@
.\" This file is part of Eclat -*- nroff -*-
-.\" Copyright (C) 2012-2014 Sergey Poznyakoff
+.\" 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
@@ -13,7 +13,7 @@
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with Eclat. If not, see <http://www.gnu.org/licenses/>.
-.TH ECLAT 1 "July 10, 2014" "ECLAT" "Eclat User Reference"
+.TH ECLAT 1 "January 19, 2015" "ECLAT" "Eclat User Reference"
.SH NAME
eclat \- EC2 Command Line Administrator Tool
.SH SYNOPSIS
@@ -26,7 +26,7 @@ eclat \- EC2 Command Line Administrator Tool
is a tool that makes it possible to manage Amazon EC2 services from
the command line. In contrast to the tools provided by Amazon itself,
.B Eclat
-does not require tons of resource-consuming libraries (\fBJava
+does not require tons of resource-consuming libraries (\fIJava
madness\fR), is very fast and efficient.
.PP
All administrative tasks are invoked through the single binary,
@@ -67,15 +67,23 @@ argument to identify the action to be performed. It then forms an
Amazon request using the rest arguments supplied to the command, and
sends it to the selected endpoint.
.PP
-An \fBendpoint\fR is a URI of the Amazon server which is supposed to
-handle the request. It is selected according to availability region,
-as set in the configuration file (the \fBdefault\-region\fR statement),
-or in the command line (the \fB\-\-region\fR option). If neither of
-these is set, the endpoint specified by the \fBdefault\-endpoint\fR
-configuration statement is used.
+.I Availability region
+specifies the region in the AWS where the requested resource is
+located. It can be set either in the configuration file (the
+\fBdefault\-region\fR statement), or in the command line (the
+\fB\-\-region\fR option).
+.PP
+If avaialbility region is not set, \fBeclat\fR attempts to get it
+from the instance store. This attempt will succeed only if it is
+run on a EC2 instance.
+.PP
+An \fIendpoint\fR is a URI of the Amazon server which is supposed to
+handle the request. It is selected according to the availability
+region. The default value is provided in the configuration file
+(using the \fBdefault\-endpoint\fR statement).
.PP
Upon completion of the action, Amazon sends back a
-.BR response :
+.IR response :
an XML document containing details about the result of the operation
performed. This document is displayed using a special format, expressed
in eclat formatting language (\fBforlan\fR for short). A set of
@@ -93,7 +101,7 @@ the option argument as its name. User-defined formats are declared in
the configuration file using the \fBdefine\-format\fR statement.
.PP
If none of these options is given, the request
-.B action name
+.I action name
is used to look up the default format to use. A default format is
defined in the configuration file using the \fBformat\fR statement.
.PP
@@ -105,7 +113,7 @@ If
.B eclat
fails to select appropriate format using this procedure, it dumps the
contents of the response on the standard output using
-.BR "path notation" ,
+.IR "path notation" ,
where each tag is identified by its name and the names of parent tags,
separated by dots.
.SH AUTHENTICATION
@@ -138,6 +146,13 @@ If the \fB\-\-access\-key\fR option is used, its argument is the
access key or tag to look for in the access file. Otherwise,
.B eclat
selects the first available key pair.
+.PP
+If the access key is not found in the access file, it is assumed to be
+a name of the \IIAM\fR role. The program then tries to obtain
+credentials from the instance store. This attempt will succeed only
+if \fBeclat\fR is run on an EC2 instance which is assigned a role upon
+its creation (see
+.BR http://docs.aws.amazon.com/IAM/latest/UserGuide/roles-usingrole-ec2instance.html )
.SH MAPS
Each amazon resource has a unique string associated with it, called
its
diff --git a/lib/.gitignore b/lib/.gitignore
index e0d9b22..e96121b 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -2,3 +2,7 @@ forlangrm.c
forlangrm.h
forlangrm.output
forlanlex.c
+jsongrm.c
+jsongrm.h
+jsongrm.output
+jsonlex.c
diff --git a/lib/Makefile.am b/lib/Makefile.am
index f3a9d42..428de49 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -42,6 +42,10 @@ libeclat_a_SOURCES=\
forlangrm.h\
forlangrm.y\
forlanlex.l\
+ json.h\
+ jsongrm.h\
+ jsongrm.y\
+ jsonlex.l\
getans.c\
getyn.c\
hmac_sha1.c\
@@ -68,8 +72,14 @@ AM_CPPFLAGS = -I$(top_srcdir)/grecs/src/ $(CURL_CFLAGS)
forlanlex.c: forlangrm.h
forlangrm.c forlangrm.h: forlangrm.y
+jsonlex.c: jsongrm.h
+jsongrm.c jsongrm.h: jsongrm.y
+
AM_YFLAGS=-tdv
AM_LFLAGS=-dp
+LEXCOMPILE = $(top_srcdir)/@GRECS_SUBDIR@/build-aux/yyrename '$(LEX) $(LFLAGS) $(AM_LFLAGS)'
+YACCCOMPILE = $(top_srcdir)/@GRECS_SUBDIR@/build-aux/yyrename '$(YACC) $(YFLAGS) $(AM_YFLAGS)'
+
diff --git a/lib/json.h b/lib/json.h
new file mode 100644
index 0000000..36ca243
--- /dev/null
+++ b/lib/json.h
@@ -0,0 +1,66 @@
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#include <grecs.h>
+
+enum json_object_type
+{
+ json_null,
+ json_bool,
+ json_number,
+ json_string,
+ json_arr,
+ json_obj
+};
+
+struct json_object;
+struct json_array {
+ size_t oc;
+ struct json_object **ov;
+};
+
+struct json_object {
+ enum json_object_type type;
+ union {
+ int b; /* json_bool */
+ double n; /* json_number */
+ char *s; /* json_string */
+ struct json_array *a; /* json_arr */
+ struct grecs_symtab *o; /* json_o */
+ } v;
+};
+
+struct json_pair {
+ char *k;
+ struct json_object *v;
+};
+
+extern char const *json_err_diag;
+extern char const *json_err_ptr;
+extern struct json_object *json_return_obj;
+
+void jsonlex_setup(char const *s, size_t l);
+void jsonlex_diag(const char *s, size_t off);
+struct json_object *json_object_create(int type);
+struct grecs_symtab *json_assoc_create(void);
+void json_object_free(struct json_object *obj);
+
+struct json_object *json_parse_string(char const *input, size_t len);
+
+struct json_object *json_object_lookup(struct json_object *obj,
+ const char *ident);
+
+
diff --git a/lib/jsongrm.y b/lib/jsongrm.y
new file mode 100644
index 0000000..398d2e7
--- /dev/null
+++ b/lib/jsongrm.y
@@ -0,0 +1,316 @@
+%{
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#include "libeclat.h"
+#include <string.h>
+#include "jsongrm.h"
+#include "json.h"
+
+struct json_object *json_return_obj;
+
+static int yyerror(char const *s);
+
+static struct json_pair *
+pairdup(struct json_pair *in)
+{
+ struct json_pair *out = grecs_malloc(sizeof(*out));
+ out->k = in->k;
+ out->v = in->v;
+ return out;
+}
+
+static void
+pairfree(void *ptr)
+{
+ struct json_pair *p = ptr;
+ free(p->k);
+ json_object_free(p->v);
+ free(p);
+}
+
+static void
+objfree(void *ptr)
+{
+ struct json_object *o = ptr;
+ json_object_free(o);
+}
+
+%}
+%token <n> T_NUMBER
+%token <s> T_STRING
+%token <b> T_BOOL
+%token T_NULL T_ERR
+
+%type <a> array
+%type <list> objects objlist pairs pairlist
+%type <p> pair
+%type <obj> object
+%type <o> assoc
+
+%union {
+ int b;
+ double n;
+ char *s;
+ struct json_array *a;
+ struct grecs_symtab *o;
+ struct json_object *obj;
+ struct grecs_list *list;
+ struct json_pair *p;
+}
+%%
+
+input : object
+ {
+ json_return_obj = $1;
+ }
+ ;
+
+object : T_NUMBER
+ {
+ $$ = json_object_create(json_number);
+ $$->v.n = $1;
+ }
+ | T_STRING
+ {
+ $$ = json_object_create(json_string);
+ $$->v.s = $1;
+ }
+ | T_BOOL
+ {
+ $$ = json_object_create(json_bool);
+ $$->v.b = $1;
+ }
+ | T_NULL
+ {
+ $$ = json_object_create(json_null);
+ }
+ | array
+ {
+ $$ = json_object_create(json_arr);
+ $$->v.a = $1;
+ }
+ | assoc
+ {
+ $$ = json_object_create(json_obj);
+ $$->v.o = $1;
+ }
+ ;
+
+array : '[' objects ']'
+ {
+ struct json_array *a = grecs_malloc(sizeof(*a));
+ if (!$2) {
+ a->oc = 0;
+ a->ov = NULL;
+ } else {
+ size_t i;
+ struct grecs_list_entry *ep;
+ a->oc = $2->count;
+ a->ov = grecs_calloc(a->oc, sizeof(a->ov));
+ for (i = 0, ep = $2->head; ep; i++, ep = ep->next) {
+ struct json_object *p = ep->data;
+ a->ov[i] = p;
+ }
+ }
+ $$ = a;
+ }
+ ;
+
+objects : /* empty */
+ {
+ $$ = NULL;
+ }
+ | objlist
+ ;
+
+objlist : object
+ {
+ $$ = grecs_list_create();
+ $$->free_entry = objfree;
+ grecs_list_append($$, $1);
+ }
+ | objlist ',' object
+ {
+ grecs_list_append($1, $3);
+ }
+ ;
+
+assoc : '{' pairs '}'
+ {
+ struct grecs_symtab *s;
+
+ s = json_assoc_create();
+ if ($2) {
+ struct grecs_list_entry *ep;
+ for (ep = $2->head; ep; ep = ep->next) {
+ struct json_pair *p = ep->data;
+ int install = 1;
+ grecs_symtab_lookup_or_install(s, p, &install);
+ if (install) {
+ p->k = NULL;
+ p->v = NULL;
+ }
+ }
+ grecs_list_free($2);
+ }
+ $$ = s;
+ }
+ ;
+
+pairs : /* empty */
+ {
+ $$ = NULL;
+ }
+ | pairlist
+ ;
+
+pairlist: pair
+ {
+ $$ = grecs_list_create();
+ $$->free_entry = pairfree;
+ grecs_list_append($$, $1);
+ }
+ | pairlist ',' pair
+ {
+ grecs_list_append($1, $3);
+ }
+ ;
+
+pair : T_STRING ':' object
+ {
+ struct json_pair *p = grecs_malloc(sizeof(*p));
+ p->k = $1;
+ p->v = $3;
+ $$ = p;
+ }
+ ;
+%%
+
+static int
+yyerror(char const *s)
+{
+ jsonlex_diag(s, 0);
+ return 0;
+}
+
+struct json_object *
+json_object_create(int type)
+{
+ struct json_object *obj = grecs_zalloc(sizeof(*obj));
+ obj->type = type;
+ return obj;
+}
+
+void
+json_object_free(struct json_object *obj)
+{
+ size_t i;
+
+ if (!obj)
+ return;
+
+ switch (obj->type) {
+ case json_bool:
+ case json_number:
+ break;
+ case json_string:
+ free(obj->v.s);
+ break;
+ case json_arr:
+ for (i = 0; i < obj->v.a->oc; i++)
+ json_object_free(obj->v.a->ov[i]);
+ free(obj->v.a);
+ break;
+ case json_obj:
+ grecs_symtab_free(obj->v.o);
+ }
+ free(obj);
+}
+
+static unsigned
+json_st_hash(void *data, unsigned long n_buckets)
+{
+ struct json_pair *p = data;
+ return grecs_hash_string(p->k, n_buckets);
+}
+
+static int
+json_st_cmp(const void *a, const void *b)
+{
+ struct json_pair const *pa = a;
+ struct json_pair const *pb = b;
+ return strcmp(pa->k, pb->k);
+}
+
+static int
+json_st_copy(void *a, void *b)
+{
+ struct json_pair *pa = a;
+ struct json_pair *pb = b;
+ memcpy(pa, pb, sizeof(*pa));
+ return 0;
+}
+
+static void
+json_st_free(void *ptr)
+{
+ struct json_pair *p = ptr;
+// printf("FREE %s\n", p->k);
+ free(p->k);
+ json_object_free(p->v);
+ free(p);
+}
+
+struct grecs_symtab *
+json_assoc_create()
+{
+ return grecs_symtab_create(sizeof(struct json_pair),
+ json_st_hash,
+ json_st_cmp,
+ json_st_copy,
+ NULL,
+ json_st_free);
+}
+
+struct json_object *
+json_parse_string(char const *input, size_t len)
+{
+ jsonlex_setup(input, len);
+ if (yyparse()) {
+ /* FIXME: error recovery */
+ return NULL;
+ }
+ return json_return_obj;
+}
+
+struct json_object *
+json_object_lookup(struct json_object *obj, const char *ident)
+{
+ struct json_pair key, *p;
+ if (!obj || obj->type != json_obj)
+ return NULL;
+ key.k = ident;
+ p = grecs_symtab_lookup_or_install(obj->v.o, &key, NULL);
+ if (!p)
+ return NULL;
+ return p->v;
+}
+
+
+
+
+
diff --git a/lib/jsonlex.l b/lib/jsonlex.l
new file mode 100644
index 0000000..322b05d
--- /dev/null
+++ b/lib/jsonlex.l
@@ -0,0 +1,183 @@
+%{
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#include "libeclat.h"
+#include "jsongrm.h"
+#include "json.h"
+
+static char const *input_ptr;
+static size_t input_size;
+
+char const *json_err_diag;
+char const *json_err_ptr;
+
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ do { \
+ size_t n = (max_size > input_size) ? input_size : max_size; \
+ if (n) { \
+ memcpy(buf, input_ptr, n); \
+ input_ptr += n; \
+ input_size -= n; \
+ } \
+ result = n; \
+ } while(0)
+
+void
+jsonlex_setup(char const *s, size_t l)
+{
+ input_ptr = s;
+ input_size = l;
+ json_err_diag = NULL;
+ json_err_ptr = NULL;
+ yy_flex_debug = 0;
+}
+
+void
+jsonlex_diag(const char *s, size_t off)
+{
+ if (!json_err_diag) {
+ json_err_diag = s;
+ json_err_ptr = input_ptr - off;
+ }
+}
+
+static int
+utf8_wctomb(char *u)
+{
+ unsigned int wc = strtoul(u, NULL, 16);
+ int count;
+ char r[6];
+
+ if (wc < 0x80)
+ count = 1;
+ else if (wc < 0x800)
+ count = 2;
+ else if (wc < 0x10000)
+ count = 3;
+ else if (wc < 0x200000)
+ count = 4;
+ else if (wc < 0x4000000)
+ count = 5;
+ else if (wc <= 0x7fffffff)
+ count = 6;
+ else
+ return -1;
+
+ switch (count) {
+ /* Note: code falls through cases! */
+ case 6:
+ r[5] = 0x80 | (wc & 0x3f);
+ wc = wc >> 6;
+ wc |= 0x4000000;
+ case 5:
+ r[4] = 0x80 | (wc & 0x3f);
+ wc = wc >> 6;
+ wc |= 0x200000;
+ case 4:
+ r[3] = 0x80 | (wc & 0x3f);
+ wc = wc >> 6;
+ wc |= 0x10000;
+ case 3:
+ r[2] = 0x80 | (wc & 0x3f);
+ wc = wc >> 6;
+ wc |= 0x800;
+ case 2:
+ r[1] = 0x80 | (wc & 0x3f);
+ wc = wc >> 6;
+ wc |= 0xc0;
+ case 1:
+ r[0] = wc;
+ }
+ grecs_line_acc_grow(r, count);
+ return count;
+}
+
+static int
+unescape(int c, int *o)
+{
+ static char transtab[] = "\\\\\"\"//b\bf\fn\nr\rt\t";
+ char *p;
+
+ for (p = transtab; *p; p += 2) {
+ if (*p == c) {
+ *o = p[1];
+ return 0;
+ }
+ }
+ return -1;
+}
+
+#define YY_SKIP_YYWRAP 1
+static int
+yywrap()
+{
+ return 1;
+}
+%}
+D [0-9]
+X [0-9a-fA-F]
+%x STR
+%%
+"-"?{D}{D}*(.{D}{D}*)?([eE][-+]?{D}{D}*)? {
+ yylval.n = strtod(yytext, NULL);
+ return T_NUMBER;
+ }
+\"[^\\\"]*\" { grecs_line_begin();
+ grecs_line_add(yytext + 1, yyleng - 2);
+ yylval.s = grecs_line_finish();
+ return T_STRING; }
+\"[^\\\"]*\\{X}{4} { BEGIN(STR);
+ grecs_line_begin();
+ grecs_line_add(yytext + 1, yyleng - 5);
+ utf8_wctomb(yytext + yyleng - 4);
+ }
+\"[^\\\"]*\\. { int c;
+ BEGIN(STR);
+ grecs_line_begin();
+ grecs_line_acc_grow(yytext + 1, yyleng - 2);
+ if (unescape(yytext[yyleng - 1], &c)) {
+ jsonlex_diag("invalid UTF-8 codepoint", 5);
+ return T_ERR;
+ }
+ grecs_line_acc_grow_char(c);
+ }
+<STR>[^\\\"]*\" { BEGIN(INITIAL);
+ if (yyleng > 1)
+ grecs_line_acc_grow(yytext, yyleng - 1);
+ yylval.s = grecs_line_finish();
+ return T_STRING; }
+<STR>[^\\\"]*\\{X}{4} {
+ grecs_line_add(yytext, yyleng - 5);
+ utf8_wctomb(yytext + yyleng - 4);
+}
+<STR>[^\\\"]*\\. {
+ int c;
+ grecs_line_acc_grow(yytext, yyleng - 2);
+ if (unescape(yytext[yyleng - 1], &c)) {
+ jsonlex_diag("invalid UTF-8 codepoint", 5);
+ return T_ERR;
+ }
+ grecs_line_acc_grow_char(c); }
+
+null { return T_NULL; }
+true { yylval.b = 1; return T_BOOL; }
+false { yylval.b = 0; return T_BOOL; }
+"{"|"}"|"["|"]"|":"|"," return yytext[0];
+[ \t\n]* ;
+. { jsonlex_diag("bogus character", 0);
+ return T_ERR; }
diff --git a/lib/libeclat.h b/lib/libeclat.h
index b7dc189..2265a7b 100644
--- a/lib/libeclat.h
+++ b/lib/libeclat.h
@@ -86,12 +86,13 @@ struct ec2_query {
struct grecs_list *headers; /* Query headers */
char *region;
char *access_key;
+ char *token;
unsigned long ttl; /* Time-to-live in seconds */
};
struct ec2_query *eclat_query_create(int flags, const char *endpoint,
const char *uri, char const *region,
- char const *access_key);
+ char const *access_key, char const *token);
void eclat_query_free(struct ec2_query *);
void eclat_query_add_param(struct ec2_query *q, const char *name,
const char *value);
diff --git a/lib/qcreat.c b/lib/qcreat.c
index 32471a0..c2898a5 100644
--- a/lib/qcreat.c
+++ b/lib/qcreat.c
@@ -29,7 +29,8 @@ ec2_param_free(void *ptr)
struct ec2_query *
eclat_query_create(int flags, const char *endpoint, const char *uri,
- char const *region, char const *access_key)
+ char const *region, char const *access_key,
+ char const *token)
{
struct ec2_query *q = grecs_zalloc(sizeof(*q));
q->flags = flags;
@@ -45,5 +46,6 @@ eclat_query_create(int flags, const char *endpoint, const char *uri,
q->ttl = 5;
q->region = grecs_strdup(region ? region : "us-east-1");
q->access_key = grecs_strdup(access_key);
+ q->token = token ? grecs_strdup(token) : NULL;
return q;
}
diff --git a/lib/reqsign.c b/lib/reqsign.c
index 397f6c8..4250907 100644
--- a/lib/reqsign.c
+++ b/lib/reqsign.c
@@ -64,6 +64,8 @@ querysign2(struct ec2_query *req, char *secret)
eclat_query_add_param(req, "AWSAccessKeyId", req->access_key);
eclat_query_add_param(req, "SignatureMethod", "HmacSHA256");
eclat_query_add_param(req, "SignatureVersion", "2");
+ if (req->token)
+ eclat_query_add_param(req, "SecurityToken", req->token);
time(&t);
strftime(tsbuf, sizeof(tsbuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t));
diff --git a/lib/yytrans b/lib/yytrans
new file mode 100644
index 0000000..9e5979c
--- /dev/null
+++ b/lib/yytrans
@@ -0,0 +1,4 @@
+forlangrm forlan_yy_
+forlanlex forlan_yy_
+jsongrm json_yy_
+jsonlex json_yy_
diff --git a/src/accfile.c b/src/accfile.c
index 3b06582..84a786b 100644
--- a/src/accfile.c
+++ b/src/accfile.c
@@ -144,7 +144,7 @@ get_access_creds(const char *id, char **access_key_ptr, char **secret_key_ptr)
int i;
glob_t g;
int rc = 1;
-
+
if (!access_file_name) {
debug(ECLAT_DEBCAT_MAIN, 1, ("no access file given"));
return 1;
diff --git a/src/cmdline.opt b/src/cmdline.opt
index 073b7b7..665ec0e 100644
--- a/src/cmdline.opt
+++ b/src/cmdline.opt
@@ -1,5 +1,5 @@
/* This file is part of Eclat.
- Copyright (C) 2012-2014 Sergey Poznyakoff.
+ 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
@@ -58,7 +58,7 @@ OPTIONS_BEGIN("eclat",
[<COMMAND [COMMAND-OPTIONS...]>],
[<gnu>],
[<nopermute>],
- [<copyright_year=2012, 2013>],
+ [<copyright_year=2012-2015>],
[<copyright_holder=Sergey Poznyakoff>],[<nopermute>])
GROUP(Selecting program mode)
diff --git a/src/ec2map.c b/src/ec2map.c
index eb1d37b..03c3016 100644
--- a/src/ec2map.c
+++ b/src/ec2map.c
@@ -147,7 +147,7 @@ ec2_map_get(int dbg, int dir, void *data, const char *key, char **return_value)
int rc;
q = eclat_query_create(use_ssl ? EC2_QF_HTTPS : 0, endpoint, "/",
- region_name, access_key);
+ region_name, access_key, security_token);
eclat_query_add_param(q, "Action", map->action);
env[0] = "key";
diff --git a/src/eclat.c b/src/eclat.c
index 88592d2..5bbe7c4 100644
--- a/src/eclat.c
+++ b/src/eclat.c
@@ -26,6 +26,7 @@ char *endpoint = "ec2.amazonaws.com";
char *signature_version = "2";
char *access_key;
char *secret_key;
+char *security_token;
char *region_name;
int use_ssl;
int ssl_verify_peer = 1;
@@ -645,7 +646,8 @@ eclat_do_command(eclat_command_env_t *env, struct eclat_command *command,
if (!(command->flags & CMD_NOQRY)) {
env->query = eclat_query_create(use_ssl ? EC2_QF_HTTPS : 0,
endpoint, "/",
- region_name, access_key);
+ region_name, access_key,
+ security_token);
}
if (command->tag)
@@ -769,9 +771,16 @@ main(int argc, char **argv)
"cannot find endpoint for region %s", region_name);
if (!secret_key) {
- if (get_access_creds(access_key, &access_key, &secret_key))
- die(EX_UNAVAILABLE,
- "cannot find authentication credentials");
+ if (get_access_creds(access_key, &access_key, &secret_key)) {
+ if (!access_key)
+ die(EX_UNAVAILABLE,
+ "cannot find authentication credentials");
+ else
+ eclat_get_instance_creds(access_key,
+ &access_key,
+ &secret_key,
+ &security_token);
+ }
}
debug(ECLAT_DEBCAT_MAIN, 1, ("using access key %s", access_key));
diff --git a/src/eclat.h b/src/eclat.h
index b9c90ba..d3c0e8d 100644
--- a/src/eclat.h
+++ b/src/eclat.h
@@ -50,6 +50,7 @@ extern char *region_name;
extern char *access_file_name;
extern char *access_key;
extern char *secret_key;
+extern char *security_token;
extern char *format_file;
extern int translation_enabled;
extern char *custom_map;
@@ -173,6 +174,9 @@ void describe_query_update(eclat_command_env_t *env, int argc, char **argv,
int eclat_send_query(CURL *curl, struct ec2_query *q);
char *eclat_get_instance_zone(void);
+void eclat_get_instance_creds(char *id,
+ char **access_key_ptr, char **secret_key_ptr,
+ char **token_ptr);
int eclat_actcmp(const char *a, const char *b);
diff --git a/src/util.c b/src/util.c
index f70f70a..88e3465 100644
--- a/src/util.c
+++ b/src/util.c
@@ -15,6 +15,7 @@
along with Eclat. If not, see <http://www.gnu.org/licenses/>. */
#include "eclat.h"
+#include "json.h"
#include <termios.h>
#include <sys/ioctl.h>
@@ -430,41 +431,74 @@ char *
eclat_get_instance_zone()
{
char *doc;
+ struct json_object *obj, *p;
char *retval = NULL;
- struct wordsplit ws;
- size_t i;
- int lev;
doc = eclat_get_instance_meta_data(URL_INST_DOCUMENT);
-
- if (wordsplit(doc, &ws,
- WRDSF_NOVAR | WRDSF_NOCMD |
- WRDSF_SQUEEZE_DELIMS |
- WRDSF_DQUOTE)) {
- die(EX_UNAVAILABLE, "wordsplit: %s", wordsplit_strerror(&ws));
- }
+
+ obj = json_parse_string(doc, strlen(doc));
+ if (!obj)
+ die(EX_DATAERR,
+ "%s: near %s",
+ json_err_diag,
+ json_err_ptr);
free(doc);
- lev = 0;
- for (i = 0; i < ws.ws_wordc; i++) {
- if (strcmp(ws.ws_wordv[i], "{") == 0)
- ++lev;
- else if (strcmp(ws.ws_wordv[i], "}") == 0)
- --lev;
- else if (lev == 1 && strcmp(ws.ws_wordv[i], "region") == 0) {
- if (i + 2 < ws.ws_wordc &&
- strcmp(ws.ws_wordv[i+1], ":") == 0) {
- size_t len;
-
- retval = grecs_strdup(ws.ws_wordv[i+2]);
- len = strlen(retval);
- if (retval[len-1] == ',')
- retval[len-1] = 0;
- break;
- }
- }
- }
- wordsplit_free(&ws);
-
+ p = json_object_lookup(obj, "region");
+ if (p && p->type == json_string)
+ retval = grecs_strdup(p->v.s);
+ json_object_free(obj);
return retval;
}
+
+#define URL_INST_CREDS\
+ "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
+
+void
+eclat_get_instance_creds(char *id, char **access_key_ptr, char **secret_key_ptr,
+ char **token_ptr)
+{
+ char *url = NULL;
+ size_t s;
+ char *doc;
+ struct json_object *obj, *p;
+ int err = 0;
+
+ s = sizeof(URL_INST_CREDS) + strlen(id);
+ url = grecs_malloc(s);
+ strcpy(url, URL_INST_CREDS);
+ strcat(url, id);
+
+ doc = eclat_get_instance_meta_data(url);
+
+ obj = json_parse_string(doc, strlen(doc));
+ if (!obj)
+ die(EX_DATAERR,
+ "%s: near %s",
+ json_err_diag,
+ json_err_ptr);
+ free(doc);
+
+ p = json_object_lookup(obj, "AccessKeyId");
+ if (p && p->type == json_string)
+ *access_key_ptr = grecs_strdup(p->v.s);
+ else
+ err = 1;
+
+ p = json_object_lookup(obj, "SecretAccessKey");
+ if (p && p->type == json_string)
+ *secret_key_ptr = grecs_strdup(p->v.s);
+ else
+ err = 1;
+
+ p = json_object_lookup(obj, "Token");
+ if (p && p->type == json_string)
+ *token_ptr = grecs_strdup(p->v.s);
+ else
+ err = 1;
+
+ json_object_free(obj);
+
+ if (err)
+ die(EX_DATAERR, "security credentials missing");
+}
diff --git a/tests/.gitignore b/tests/.gitignore
index 0f9880b..08adbd4 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -6,6 +6,7 @@ testsuite
testsuite.dir
testsuite.log
tforlan
+tjson
thmac
trws
turlenc
diff --gi