Skip to content

Instantly share code, notes, and snippets.

@mpeven
Created July 7, 2016 03:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mpeven/18d1cf76fcb7079b7adaa18ef5137ff0 to your computer and use it in GitHub Desktop.
Save mpeven/18d1cf76fcb7079b7adaa18ef5137ff0 to your computer and use it in GitHub Desktop.
/*
* 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