/* 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"
#include "json.h"
#include
#include
#include
int translation_enabled;
char *custom_map;
void
translate_ids(int argc, char **argv, const char *mapname)
{
int i;
struct eclat_map *map;
char *val;
char *q, *realname;
int dir;
int rc;
if (!translation_enabled || argc == 0)
return;
if (custom_map)
mapname = custom_map;
dir = eclat_map_name_split(mapname, &realname, &q);
if (dir == -1)
die(EX_USAGE, "bad qualifier: %s", q);
map = eclat_map_lookup(realname);
if (!map) {
debug(ECLAT_DEBCAT_MAIN, 1,
("no such map: %s", realname));
return;
}
if (eclat_map_open(map) != eclat_map_ok)
die(EX_UNAVAILABLE, "failed to open map %s", realname);
free(realname);
for (i = 0; i < argc; i++) {
if (!strchr(argv[i], '=')) {
switch (rc = eclat_map_get(map, dir, argv[i], &val)) {
case eclat_map_ok:
argv[i] = val;
break;
case eclat_map_not_found:
debug(ECLAT_DEBCAT_MAIN, 1,
("%s not found in map %s",
argv[i], mapname));
break;
default:
die(EX_UNAVAILABLE, "cannot translate %s: %s",
argv[i], eclat_map_strerror(rc));
}
}
}
}
char *
eclat_stpcpy(char *p, char *q)
{
while (*p = *q++)
++p;
return p;
}
#define RESOURCE_ID_PFX "resource-id="
#define RESOURCE_ID_LEN (sizeof(RESOURCE_ID_PFX) - 1)
void
translate_resource_ids(int argc, char **argv)
{
int i, j, rc, ecnt;
size_t len;
struct eclat_map *map;
char *val, *p;
struct wordsplit ws;
int wsflags = WRDSF_DEFFLAGS|WRDSF_DELIM;
if (!translation_enabled || argc == 0)
return;
ws.ws_delim = ",";
for (i = 0; i < argc; i++) {
if (strncmp(argv[i], RESOURCE_ID_PFX, RESOURCE_ID_LEN))
continue;
if (wordsplit(argv[i] + RESOURCE_ID_LEN, &ws, wsflags))
die(EX_SOFTWARE,
"error expanding argument %s: %s",
argv[i] + RESOURCE_ID_LEN,
wordsplit_strerror(&ws));
wsflags |= WRDSF_REUSE;
for (j = 0, ecnt = 0; j < ws.ws_wordc; j++) {
if (!(p = strchr(ws.ws_wordv[j], ':')))
continue;
*p++ = 0;
map = eclat_map_lookup(ws.ws_wordv[j]);
if (!map)
die(EX_UNAVAILABLE, "no such map: %s",
ws.ws_wordv[j]);
if (eclat_map_open(map) != eclat_map_ok)
die(EX_UNAVAILABLE,
"failed to open map %s", ws.ws_wordv[j]);
rc = eclat_map_get(map, MAP_DIR, p, &val);
if (rc != eclat_map_ok) {
die(EX_UNAVAILABLE,
"cannot translate %s using map %s: %s",
p, ws.ws_wordv[j], eclat_map_strerror(rc));
}
free(ws.ws_wordv[j]);
ws.ws_wordv[j] = val;
ecnt++;
}
if (ecnt == 0)
continue;
len = RESOURCE_ID_LEN + ws.ws_wordc - 1;
for (j = 0; j < ws.ws_wordc; j++)
len += strlen(ws.ws_wordv[j]);
val = grecs_malloc(len + 1);
strcpy(val, RESOURCE_ID_PFX);
p = val + RESOURCE_ID_LEN;
for (j = 0; j < ws.ws_wordc; j++) {
if (j)
*p++ = ',';
p = eclat_stpcpy(p, ws.ws_wordv[j]);
}
argv[i] = val;
}
if (wsflags & WRDSF_REUSE)
wordsplit_free(&ws);
}
int
get_scr_cols()
{
struct winsize ws;
ws.ws_col = ws.ws_row = 0;
if ((ioctl(1, TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_col == 0) {
const char *p = getenv ("COLUMNS");
if (p)
ws.ws_col = strtol(p, NULL, 10);
}
return ws.ws_col ? ws.ws_col : 80;
}
void
describe_request_update(eclat_command_env_t *env, int argc, char **argv,
const char *uparm, int n_in, int *n_out)
{
int i, j, k;
struct ec2_request *q = env->request;
char *bufptr = NULL;
size_t bufsize = 0;
struct wordsplit ws;
int wsflags;
int upn = 0;
ws.ws_delim = ",";
wsflags = WRDSF_DEFFLAGS | WRDSF_DELIM;
for (i = 0, j = n_in; i < argc; i++) {
char *p = strchr(argv[i], '=');
if (!p) {
if (uparm) {
grecs_asprintf(&bufptr, &bufsize,
"%s.%d", uparm, upn++);
eclat_request_add_param(q, bufptr, argv[i]);
continue;
}
die(EX_USAGE, "malformed filter: %s", argv[i]);
}
*p++ = 0;
grecs_asprintf(&bufptr, &bufsize, "Filter.%d.Name", j);
eclat_request_add_param(q, bufptr, argv[i]);
if (wordsplit(p, &ws, wsflags))
die(EX_SOFTWARE, "wordsplit failed at \"%s\": %s",
p, wordsplit_strerror(&ws));
wsflags |= WRDSF_REUSE;
for (k = 0; k < ws.ws_wordc; k++) {
grecs_asprintf(&bufptr, &bufsize, "Filter.%d.Value.%d",
j, k+1);
eclat_request_add_param(q, bufptr, ws.ws_wordv[k]);
}
++j;
}
if (wsflags & WRDSF_REUSE)
wordsplit_free(&ws);
free(bufptr);
if (n_out)
*n_out = j;
}
void
describe_request_create(eclat_command_env_t *env, int argc, char **argv,
const char *uparm)
{
describe_request_update(env, argc, argv, uparm, 1, NULL);
}
int
eclat_send_request(CURL *curl, struct ec2_request *req)
{
char *url;
CURLcode res;
int rc = 0;
struct curl_slist *headers = NULL;
/* Prepare the request */
eclat_request_sign(req, secret_key, signature_version);
url = eclat_request_to_url(req, NULL);
curl_easy_setopt(curl, CURLOPT_URL, url);
debug(ECLAT_DEBCAT_MAIN, 1, ("using URL: %s", url));
free(url);
if (req->headers) {
struct grecs_list_entry *ep;
struct grecs_txtacc *acc;
int rc;
acc = grecs_txtacc_create();
for (ep = req->headers->head; ep; ep = ep->next) {
struct ec2_param *p = ep->data;
char *str;
grecs_txtacc_grow_string(acc, p->name);
grecs_txtacc_grow_char(acc, ':');
grecs_txtacc_grow_string(acc, p->value);
grecs_txtacc_grow_char(acc, 0);
str = grecs_txtacc_finish(acc, 0);
debug(ECLAT_DEBCAT_MAIN, 1, ("HDR: %s", str));
headers = curl_slist_append(headers, str);
grecs_txtacc_free_string(acc, str);
}
rc = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
grecs_txtacc_free(acc);
if (rc)
die(EX_SOFTWARE,
"failed to add headers: %s",
curl_easy_strerror(rc));
}
if (dry_run_mode)
debug(ECLAT_DEBCAT_MAIN, 1, ("not sending request"));
else {
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
err("CURL: %s", curl_easy_strerror(res));
rc = 1;
}
}
eclat_request_free(req);
curl_slist_free_all(headers);
return rc;
}
int
eclat_actcmp(const char *a, const char *b)
{
int rc = 0;
enum { cmp_init, cmp_norm, cmp_upca, cmp_upcb } state = cmp_init;
while (rc == 0) {
if (!*a)
return *b ? - *b : 0;
if (!*b)
return *a ? *a : 0;
if (*a == '-') {
a++;
if (state == cmp_norm)
state = cmp_upca;
else
state = cmp_norm;
} else if (*b == '-') {
b++;
if (state == cmp_norm)
state = cmp_upcb;
else
state = cmp_norm;
} else {
switch (state) {
case cmp_init:
rc = toupper(*a) - toupper(*b);
break;
case cmp_norm:
rc = *a - *b;
break;
case cmp_upca:
rc = toupper(*a) - *b;
break;
case cmp_upcb:
rc = *a - toupper(*b);
break;
}
a++;
b++;
state = cmp_norm;
}
}
return rc;
}
char **available_attrs;
void
list_attrs(FILE *fp)
{
size_t ncols = get_scr_cols();
int i, col, len;
char *delim = "";
fprintf(fp, "Available attributes are:\n");
col = 0;
for (i = 0; available_attrs[i]; i++) {
len = strlen(delim) + strlen(available_attrs[i]);
if (col + len > ncols) {
fprintf(fp, ",\n%s", available_attrs[i]);
col = strlen(available_attrs[i]);
} else
col += fprintf(fp, "%s%s", delim, available_attrs[i]);
delim = ", ";
}
fputc('\n', fp);
fputc('\n', fp);
}
char *
canonattrname(char **attrs, const char *arg, char *delim, size_t *plen)
{
size_t len = strlen(arg);
int i;
for (i = 0; attrs[i]; i++) {
size_t alen = delim ? strcspn(attrs[i], delim)
: strlen(attrs[i]);
if (alen == len && strncasecmp(arg, attrs[i], len) == 0) {
if (plen)
*plen = len;
return attrs[i];
}
}
return NULL;
}
char *
read_file(const char *file)
{
char *buf = NULL;
if (strcmp(file, "-") == 0) {
struct grecs_txtacc *acc = grecs_txtacc_create();
char inbuf[4096];
size_t n;
while (n = fread(inbuf, 1, sizeof(inbuf), stdin))
grecs_txtacc_grow(acc, inbuf, n);
grecs_txtacc_grow_char(acc, 0);
if (ferror(stdin))
die(EX_NOINPUT, "read error");
grecs_txtacc_grow_char(acc, 0);
buf = grecs_txtacc_finish(acc, 1);
grecs_txtacc_free(acc);
} else {
struct stat st;
FILE *fp;
if (stat(file, &st))
die(EX_USAGE, "cannot stat file %s: %s", file,
strerror(errno));
/* FIXME: Use limits.h to check st.st_size */
buf = grecs_malloc(st.st_size+1);
fp = fopen(file, "r");
if (!fp)
die(EX_NOINPUT, "cannot open file %s: %s", file,
strerror(errno));
if (fread(buf, st.st_size, 1, fp) != 1)
die(EX_NOINPUT, "error reading from %s: %s", file,
strerror(errno));
fclose(fp);
buf[st.st_size] = 0;
}
return buf;
}
char *instance_store_base_url = "http://169.254.169.254/latest";
unsigned short instance_store_port;
char *instance_store_document_path = "dynamic/instance-identity/document";
char *instance_store_credentials_path = "meta-data/iam/security-credentials";
static CURL *
get_curl(struct grecs_txtacc *acc)
{
CURLcode res;
CURL *curl = instance_store_curl_new(acc);
eclat_set_curl_trace(curl, debug_level(ECLAT_DEBCAT_CURL));
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
if (instance_store_port)
curl_easy_setopt(curl, CURLOPT_PORT,
(long) instance_store_port);
return curl;
}
char *
eclat_get_instance_zone()
{
char *doc;
struct json_object *obj, *p;
char *retval = NULL;
CURL *curl;
char *url;
struct grecs_txtacc *acc;
acc = grecs_txtacc_create();
curl = get_curl(acc);
url = path_concat(instance_store_base_url,
instance_store_document_path);
if (instance_store_read(url, curl))
doc = NULL;
else {
grecs_txtacc_grow_char(acc, 0);
doc = grecs_txtacc_finish(acc, 0);
}
free(url);
curl_easy_cleanup(curl);
if (!doc)
return NULL;
obj = json_parse_string(doc, strlen(doc));
if (!obj)
die(EX_DATAERR,
"%s: near %s",
json_err_diag,
json_err_ptr);
p = json_object_lookup(obj, "region");
if (p && p->type == json_string)
retval = grecs_strdup(p->v.s);
grecs_txtacc_free(acc);
json_object_free(obj);
return retval;
}
void
eclat_get_instance_creds(char *id, char **access_key_ptr, char **secret_key_ptr,
char **token_ptr)
{
CURL *curl;
char *url = NULL;
char *s;
char *doc;
struct json_object *obj, *p;
int err = 0;
struct grecs_txtacc *acc;
acc = grecs_txtacc_create();
curl = get_curl(acc);
url = path_concat(instance_store_base_url,
instance_store_credentials_path);
if (id) {
s = url;
url = path_concat(s, id);
free(s);
}
if (instance_store_read(url, curl))
die(EX_UNAVAILABLE, "url %s: not found ", url);
else {
grecs_txtacc_grow_char(acc, 0);
doc = grecs_txtacc_finish(acc, 0);
}
if (!id) {
size_t len = strcspn(doc, "\r\n");
doc[len] = 0;
s = url;
url = path_concat(s, doc);
free(s);
if (instance_store_read(url, curl))
die(EX_UNAVAILABLE, "url %s: not found ", url);
else {
grecs_txtacc_grow_char(acc, 0);
doc = grecs_txtacc_finish(acc, 0);
}
}
free(url);
obj = json_parse_string(doc, strlen(doc));
if (!obj)
die(EX_DATAERR,
"%s: near %s",
json_err_diag,
json_err_ptr);
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;
grecs_txtacc_free(acc);
json_object_free(obj);
if (err)
die(EX_DATAERR, "security credentials missing");
}