Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b3cfda9

Browse files
committed
Allow ability to call SDK CLI directly
1 parent c6e3d8d commit b3cfda9

File tree

1 file changed

+90
-26
lines changed

1 file changed

+90
-26
lines changed

circa.c

Lines changed: 90 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <stdio.h>
66
#include <stdlib.h>
77
#include <string.h>
8+
#include <sys/stat.h>
89
#include <unistd.h>
910

1011
#include <curl/curl.h>
@@ -16,7 +17,13 @@
1617
"%s/emissions/forecasts/current?location=%s&dataEndAt=%s&windowSize=%d"
1718
#define URL_SIZE 256
1819

20+
#define ISO8601_CLI_FORMAT "%4d-%02d-%02dT%02d:%02d:%02dZ"
21+
#define ISO8601_CLI_SIZE 22
22+
#define CLI_FORMAT "%s emissions-forecasts -l %s -e %s -w %d"
23+
#define CLI_SIZE 256
24+
1925
typedef struct Params_t {
26+
bool cli;
2027
char *url;
2128
char *location;
2229
int window;
@@ -74,6 +81,7 @@ void home_path(char *file, char *path) {
7481

7582
void default_args(params_t *params) {
7683
// copy strings so they can be freed if overridden by config files
84+
params->cli = false;
7785
params->url = strdup("https://carbon-aware-api.azurewebsites.net");
7886
params->location = strdup("eastus");
7987
params->window = 30;
@@ -179,6 +187,14 @@ bool parse_args(int argc, char *argv[], params_t *params) {
179187
}
180188
i++;
181189

190+
// check to see if URL is path to carbon aware cli executable
191+
if (params->url[0] != 'h') {
192+
struct stat sb;
193+
if (stat(params->url, &sb) == 0 && sb.st_mode & S_IXUSR) {
194+
params->cli = true;
195+
}
196+
}
197+
182198
// anything else is the optional command and its params
183199
params->command = i;
184200
return true;
@@ -199,31 +215,32 @@ the program will just block until the best time.\n\n",
199215
-u <api url> url prefix of Carbon Aware API server to consult\n");
200216
}
201217

202-
void format_url(params_t *params, char *url) {
218+
void format_params(params_t *params, size_t iso8601_size, char *iso8601_format,
219+
size_t len, char *format, char *param_str) {
203220
time_t now;
204221
struct tm *time_tm;
205-
char end[ISO8601_URL_SIZE];
222+
char end[iso8601_size];
206223
int size;
207224

208225
time(&now);
209226
// add time this way isn't necessarily portable!
210227
now = now + params->hours * 60 * 60;
211228
time_tm = gmtime(&now);
212-
size = snprintf(end, ISO8601_URL_SIZE, ISO8601_URL_FORMAT,
213-
time_tm->tm_year + 1900, // years from 1900
214-
time_tm->tm_mon + 1, // 0-based
215-
time_tm->tm_mday, // 1-based
216-
time_tm->tm_hour, time_tm->tm_min, 0);
229+
size = snprintf(end, iso8601_size, iso8601_format,
230+
time_tm->tm_year + 1900, // years from 1900
231+
time_tm->tm_mon + 1, // 0-based
232+
time_tm->tm_mday, // 1-based
233+
time_tm->tm_hour, time_tm->tm_min, 0);
217234

218-
if (size > ISO8601_URL_SIZE) {
235+
if (size > iso8601_size) {
219236
// date string was truncated which should never happen
220237
fprintf(stderr, "Warning: end date corrupted (iso8601 date truncated)\n");
221238
}
222239

223240
printf("Requesting %d min duration window before %02d:%02d UTC in %s\n",
224241
params->window, time_tm->tm_hour, time_tm->tm_min, params->location);
225242

226-
snprintf(url, URL_SIZE, URL_FORMAT, params->url, params->location, end,
243+
snprintf(param_str, len, format, params->url, params->location, end,
227244
params->window);
228245
}
229246

@@ -240,7 +257,7 @@ static size_t write_response(void *contents, size_t size, size_t nmemb,
240257
char *ptr = realloc(mem->text, mem->size + realsize + 1);
241258
if (!ptr) {
242259
// out of memory!
243-
printf("not enough memory (realloc returned NULL)\n");
260+
fprintf(stderr, "not enough memory (realloc returned NULL)\n");
244261
return 0;
245262
}
246263

@@ -252,7 +269,8 @@ static size_t write_response(void *contents, size_t size, size_t nmemb,
252269
return realsize;
253270
}
254271

255-
void call_api(char *url, void (*extra_data)(response_t *, void *), void *data) {
272+
void call_api(char *url, void (*extract_data)(response_t *, void *),
273+
void *data) {
256274
CURL *curl;
257275
CURLcode res;
258276
response_t response;
@@ -273,7 +291,7 @@ void call_api(char *url, void (*extra_data)(response_t *, void *), void *data) {
273291
fprintf(stderr, "curl_easy_perform() failed: %s\n",
274292
curl_easy_strerror(res));
275293
} else {
276-
extra_data(&response, data);
294+
extract_data(&response, data);
277295
}
278296
curl_easy_cleanup(curl);
279297
}
@@ -283,12 +301,43 @@ void call_api(char *url, void (*extra_data)(response_t *, void *), void *data) {
283301
curl_global_cleanup();
284302
}
285303

304+
void call_cli(char *cmd, void (*extract_data)(response_t *, void *),
305+
void *data) {
306+
const size_t BUFSIZE = 255;
307+
response_t response;
308+
309+
response.text = malloc(1); // will be grown as needed by the realloc below
310+
response.size = 0; // no data at this point
311+
312+
char buf[BUFSIZE];
313+
FILE *fp;
314+
315+
if ((fp = popen(cmd, "r")) == NULL) {
316+
fprintf(stderr, "Error opening pipe!\n");
317+
return;
318+
}
319+
320+
while (fgets(buf, BUFSIZE, fp) != NULL) {
321+
size_t read = strnlen(buf, BUFSIZE);
322+
write_response(buf, sizeof(char), read, &response);
323+
}
324+
325+
if (pclose(fp)) {
326+
fprintf(stderr, "Command not found or exited with error status\n");
327+
return;
328+
}
329+
330+
extract_data(&response, data);
331+
332+
free(response.text);
333+
}
334+
286335
void parse_response(response_t *response, void *wait_seconds) {
287336
size_t i;
288337
json_t *root;
289338
json_error_t error;
290339

291-
printf("Carbon Aware API returned %lu bytes\n",
340+
printf("Carbon Aware SDK returned %lu bytes\n",
292341
(unsigned long)response->size);
293342
root = json_loads(response->text, 0, &error);
294343

@@ -317,9 +366,13 @@ void parse_response(response_t *response, void *wait_seconds) {
317366

318367
optimals = json_object_get(data, "optimalDataPoints");
319368
if (!json_is_array(optimals)) {
320-
fprintf(stderr, "error: optimalDataPoints is not an array\n");
321-
json_decref(root);
322-
return;
369+
// might be cli and in capitals
370+
optimals = json_object_get(data, "OptimalDataPoints");
371+
if (!json_is_array(optimals)) {
372+
fprintf(stderr, "error: optimalDataPoints is not an array\n");
373+
json_decref(root);
374+
return;
375+
}
323376
}
324377

325378
// just look at the first for now
@@ -335,12 +388,16 @@ void parse_response(response_t *response, void *wait_seconds) {
335388

336389
timestamp = json_object_get(optimal, "timestamp");
337390
if (!json_is_string(timestamp)) {
338-
fprintf(stderr,
339-
"error: forecast %d optimalDataPoints %d timestamp is not a "
340-
"string\n",
341-
(int)i, (int)0);
342-
json_decref(root);
343-
return;
391+
// might be cli with different name
392+
timestamp = json_object_get(optimal, "Time");
393+
if (!json_is_string(timestamp)) {
394+
fprintf(stderr,
395+
"error: forecast %d optimalDataPoints %d timestamp is not a "
396+
"string\n",
397+
(int)i, (int)0);
398+
json_decref(root);
399+
return;
400+
}
344401
}
345402

346403
timestamp_text = json_string_value(timestamp);
@@ -380,11 +437,18 @@ int main(int argc, char *argv[]) {
380437
return 1;
381438
}
382439

383-
char url[URL_SIZE];
384-
format_url(&params, url);
385-
386440
double wait_seconds = -DBL_MAX;
387-
call_api(url, parse_response, &wait_seconds);
441+
if (params.cli) {
442+
char cli[CLI_SIZE];
443+
format_params(&params, ISO8601_CLI_SIZE, ISO8601_CLI_FORMAT, CLI_SIZE,
444+
CLI_FORMAT, cli);
445+
call_cli(cli, parse_response, &wait_seconds);
446+
} else {
447+
char url[URL_SIZE];
448+
format_params(&params, ISO8601_URL_SIZE, ISO8601_URL_FORMAT, URL_SIZE,
449+
URL_FORMAT, url);
450+
call_api(url, parse_response, &wait_seconds);
451+
}
388452

389453
if (wait_seconds > 5 * 60) {
390454
printf("Sleeping for %.2f minutes\n", wait_seconds / 60);

0 commit comments

Comments
 (0)