Created
July 7, 2016 03:31
-
-
Save mpeven/18d1cf76fcb7079b7adaa18ef5137ff0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Device Application | |
* ------------------ | |
* This is the brains of the device. | |
* It serves 2 functions: | |
* 1) Updating the backend with all the relevant data. | |
* 2) Changing settings when certain things happen | |
* (ULPM, turning access to data on/off) | |
*/ | |
/* | |
* Installation Instructions | |
* ------------------------- | |
* Copy all files over. Might need to copy the json-c library into this | |
* directory and compile it. This is how to do that: | |
mkdir json-c && curl -sL https://github.com/json-c/json-c/archive/json-c-0.12.1-20160607.tar.gz | tar xz -C json-c --strip-components 1 && cd json-c && sh autogen.sh && ./configure && make && make install && cd ../ | |
* | |
* Then compile this directory with "make" and run with "./prog" | |
*/ | |
/* Standard includes */ | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <unistd.h> | |
/* libcurl (http://curl.haxx.se/libcurl/c) */ | |
#include <curl/curl.h> | |
/* json-c (https://github.com/json-c/json-c) */ | |
#include <json-c/json.h> | |
/* Global variables */ | |
static const unsigned int LOOP_TIMER = 5; | |
static const char *GET_USER_ENDPOINT = "http://backend-django-env.dpsrk7dr9t.us-west-2.elasticbeanstalk.com/api/users/278/"; | |
static const char *PUT_UPDATE_DATA_ENDPOINT = "http://backend-django-env.dpsrk7dr9t.us-west-2.elasticbeanstalk.com/api/users/ABC123/update_data_used/"; | |
static const char *VNSTAT_DELETE = "vnstat -i en1 --delete --force > /dev/null 2>&1"; | |
static const char *VNSTAT_UPDATE = "vnstat -i en1 -u > /dev/null 2>&1"; | |
static const char *VNSTAT_OUTPUT = "vnstat -i en1 --json"; | |
/* Structs */ | |
struct string { | |
char *ptr; | |
size_t len; | |
}; | |
/* Function declarations */ | |
size_t writefunc(); | |
long long get_data_purchased(); | |
long long get_backend_data_used(); | |
long long get_session_data_used(); | |
void update_backend(long long); | |
/* Main */ | |
int main(void) | |
{ | |
/* Delete all pre-existing vnstat data and start fresh */ | |
system(VNSTAT_DELETE); | |
/* Start saving vnstat data right away */ | |
// DONE --> Will automatically happen | |
/* Create initial device object */ | |
long long data_purchased = get_data_purchased(); | |
long long backend_data_used = get_backend_data_used(); | |
long long previous_data_used = backend_data_used; | |
long long data_used_diff = 0; | |
long long session_data_used = get_session_data_used(); | |
long user_id = 278; | |
char device_id[] = "ABC123"; | |
printf("--- initial values ---\n"); | |
printf("data_purchased: %lli\n", data_purchased); | |
printf("previous_data_used: %lli\n", previous_data_used); | |
printf("session_data_used: %lli\n", session_data_used); | |
printf("user_id: %li\n", user_id); | |
printf("device_id: %s\n", device_id); | |
printf("----------------------\n"); | |
/* Update the backend every LOOP_TIMER seconds */ | |
// TODO - not finished | |
while(1) { | |
system(VNSTAT_UPDATE); | |
data_purchased = get_data_purchased(); | |
session_data_used = get_session_data_used(); | |
backend_data_used = get_backend_data_used(); | |
data_used_diff = (previous_data_used + session_data_used) - backend_data_used; | |
printf("session_data_used: %lli\n", session_data_used); | |
printf("backend_data_used: %lli\n", backend_data_used); | |
/* | |
* Update the backend by sending it the difference between the total | |
* data used on the device since it was registered and the data used | |
* amount stored on the backend | |
*/ | |
update_backend(data_used_diff); | |
sleep(LOOP_TIMER); | |
}; | |
/* End gracefully on command line */ | |
return 0; | |
} | |
/* | |
* Writes a stream into the response string struct, reallocates memory as | |
* necessary | |
*/ | |
size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *response) | |
{ | |
size_t new_len = response->len + size*nmemb; | |
response->ptr = realloc(response->ptr, new_len+1); | |
if (response->ptr == NULL) { | |
fprintf(stderr, "realloc() failed\n"); | |
exit(EXIT_FAILURE); | |
} | |
memcpy(response->ptr+response->len, ptr, size*nmemb); | |
response->ptr[new_len] = '\0'; | |
response->len = new_len; | |
return size*nmemb; | |
} | |
/* | |
* Asks the backend for the user object and returns amount of data purchased | |
* If there is no internet, or the API call fails, it returns 0 | |
* | |
* @returns data purchased in bytes or 0 if no connection | |
*/ | |
long long get_data_purchased() | |
{ | |
CURL *curl; | |
curl = curl_easy_init(); | |
if(curl) { | |
struct string response; | |
response.len = 0; | |
response.ptr = malloc(0); | |
if (response.ptr == NULL) { | |
fprintf(stderr, "malloc() failed\n"); | |
exit(EXIT_FAILURE); | |
} | |
response.ptr[0] = '\0'; | |
curl_easy_setopt(curl, CURLOPT_URL, GET_USER_ENDPOINT); | |
/* Set a callback function to receive incoming data chunks */ | |
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); | |
/* The callback takes a user defined argument (the string) */ | |
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); | |
/* Perform request */ | |
curl_easy_perform(curl); | |
/* Create a json object and then parse for "data_purchased" */ | |
struct json_object *user_object; | |
struct json_object *data_purchased_object; | |
user_object = json_tokener_parse(response.ptr); | |
json_object_object_get_ex(user_object, "data_purchased", &data_purchased_object); | |
/* Get the json_object as a string then convert into a long long */ | |
const char *data_purchased_string = json_object_to_json_string(data_purchased_object); | |
long long data_purchased = atoll(data_purchased_string); | |
/* always cleanup */ | |
free(response.ptr); | |
curl_easy_cleanup(curl); | |
return data_purchased; | |
} | |
return 0; | |
} | |
/* | |
* Asks the backend for the user object and returns amount of data used | |
* If there is no internet, or the API call fails, it returns 0 | |
* | |
* @returns data used in bytes or 0 if no connection | |
*/ | |
long long get_backend_data_used() | |
{ | |
CURL *curl; | |
curl = curl_easy_init(); | |
if(curl) { | |
struct string response; | |
response.len = 0; | |
response.ptr = malloc(0); | |
if (response.ptr == NULL) { | |
fprintf(stderr, "malloc() failed\n"); | |
exit(EXIT_FAILURE); | |
} | |
response.ptr[0] = '\0'; | |
curl_easy_setopt(curl, CURLOPT_URL, GET_USER_ENDPOINT); | |
/* Set a callback function to receive incoming data chunks */ | |
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); | |
/* The callback takes a user defined argument (the string) */ | |
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); | |
/* Perform request */ | |
curl_easy_perform(curl); | |
/* Create a json object and then parse for "data_purchased" */ | |
struct json_object *user_object; | |
struct json_object *data_used_object; | |
user_object = json_tokener_parse(response.ptr); | |
json_object_object_get_ex(user_object, "data_used", &data_used_object); | |
/* Get the json_object as a string then convert into a long long */ | |
const char *data_used_string = json_object_to_json_string(data_used_object); | |
long long data_used = atoll(data_used_string); | |
/* always cleanup */ | |
free(response.ptr); | |
curl_easy_cleanup(curl); | |
return data_used; | |
} | |
return 0; | |
} | |
/* | |
* Makes a system call to vnstat and returns the total traffic over the | |
* specified interface | |
* | |
* @returns traffic (tx + rx) over a certain interface in bytes | |
*/ | |
long long get_session_data_used() | |
{ | |
// First update vnstat database | |
system(VNSTAT_UPDATE); | |
FILE *in; | |
extern FILE *popen(); | |
char vnstat_json_string[512]; | |
if(!(in = popen(VNSTAT_OUTPUT, "r"))) { | |
exit(1); | |
} | |
fgets(vnstat_json_string, sizeof(vnstat_json_string), in); | |
pclose(in); | |
/* Turn vnstat output into json object */ | |
struct json_object *vnstat_object; | |
struct json_object *interfaces_object; | |
struct json_object *traffic_object; | |
struct json_object *total_object; | |
struct json_object *en1_object; | |
vnstat_object = json_tokener_parse(vnstat_json_string); | |
json_object_object_get_ex(vnstat_object, "interfaces", &interfaces_object); | |
en1_object = json_object_array_get_idx(interfaces_object, 0); | |
json_object_object_get_ex(en1_object, "traffic", &traffic_object); | |
json_object_object_get_ex(traffic_object, "total", &total_object); | |
struct json_object *data_tx_object; | |
struct json_object *data_rx_object; | |
json_object_object_get_ex(total_object, "tx", &data_tx_object); | |
json_object_object_get_ex(total_object, "rx", &data_rx_object); | |
const char *data_tx_string = json_object_to_json_string(data_tx_object); | |
const char *data_rx_string = json_object_to_json_string(data_rx_object); | |
long long data_tx_kib = atoll(data_tx_string); | |
long long data_rx_kib = atoll(data_rx_string); | |
long long total_traffic_bytes = (data_tx_kib + data_rx_kib) * 1024; | |
return total_traffic_bytes; | |
} | |
void update_backend(long long data_diff) | |
{ | |
CURL *curl; | |
curl = curl_easy_init(); | |
if (curl) { | |
/* Build objects for PUT request */ | |
json_object *json; | |
json = json_object_new_object(); | |
json_object_object_add(json, "data_used", json_object_new_int(data_diff)); | |
struct curl_slist *headers = NULL; | |
headers = curl_slist_append(headers, "Content-Type: application/json"); | |
/* Make request */ | |
curl_easy_setopt(curl, CURLOPT_URL, PUT_UPDATE_DATA_ENDPOINT); | |
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // Set headers | |
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); // PUT request | |
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_object_to_json_string(json)); | |
FILE *devnull = fopen("/dev/null","w"); | |
curl_easy_setopt(curl, CURLOPT_WRITEDATA, devnull); // Write to nothing | |
curl_easy_perform(curl); | |
curl_slist_free_all(headers); | |
curl_easy_cleanup(curl); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment