/* * 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 #include #include #include /* libcurl (http://curl.haxx.se/libcurl/c) */ #include /* json-c (https://github.com/json-c/json-c) */ #include /* 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); } }