/* 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 */ if (req->flags & EC2_RF_POST) { eclat_request_finalize(req); curl_easy_setopt(curl, CURLOPT_POST, 1); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req->postdata); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(req->postdata)); } eclat_request_sign(req, secret_key, signature_version); url = eclat_request_to_url(req); 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 (req->flags & EC2_RF_POST) debug(ECLAT_DEBCAT_MAIN, 1, ("DATA: %s", req->postdata)); 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"); }