diff options
Diffstat (limited to 'vnfs/VESreporting_vFW5.0_DANOS/evel/evel-library/code/evel_library/evel_event_mgr.c')
-rw-r--r-- | vnfs/VESreporting_vFW5.0_DANOS/evel/evel-library/code/evel_library/evel_event_mgr.c | 1664 |
1 files changed, 1664 insertions, 0 deletions
diff --git a/vnfs/VESreporting_vFW5.0_DANOS/evel/evel-library/code/evel_library/evel_event_mgr.c b/vnfs/VESreporting_vFW5.0_DANOS/evel/evel-library/code/evel_library/evel_event_mgr.c new file mode 100644 index 00000000..cc676a6f --- /dev/null +++ b/vnfs/VESreporting_vFW5.0_DANOS/evel/evel-library/code/evel_library/evel_event_mgr.c @@ -0,0 +1,1664 @@ +/*************************************************************************//** + * + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * + * Unless otherwise specified, all software contained herein is + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + ****************************************************************************/ + +/**************************************************************************//** + * @file + * Event Manager + * + * Simple event manager that is responsible for taking events (Heartbeats, + * Faults and Measurements) from the ring-buffer and posting them to the API. + * + ****************************************************************************/ + +#include <string.h> +#include <assert.h> +#include <stdlib.h> +#include <pthread.h> + +#include <curl/curl.h> + +#include "evel.h" +#include "evel_internal.h" +#include "ring_buffer.h" +#include "evel_throttle.h" + +/**************************************************************************//** + * How long we're prepared to wait for the API service to respond in + * seconds. + *****************************************************************************/ +static const int EVEL_API_TIMEOUT = 5; + +/**************************************************************************//** + * Wait time if both the collectors are not responding + *****************************************************************************/ +static const int EVEL_COLLECTOR_RECONNECTION_WAIT_TIME = 120; + +/*****************************************************************************/ +/* Prototypes of locally scoped functions. */ +/*****************************************************************************/ +static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp); +static void * event_handler(void *arg); +static bool evel_handle_response_tokens(const MEMORY_CHUNK * const chunk, + const jsmntok_t * const json_tokens, + const int num_tokens, + MEMORY_CHUNK * const post); +static bool evel_tokens_match_command_list(const MEMORY_CHUNK * const chunk, + const jsmntok_t * const json_token, + const int num_tokens); +static bool evel_token_equals_string(const MEMORY_CHUNK * const chunk, + const jsmntok_t * const json_token, + const char * check_string); +static EVEL_ERR_CODES evel_setup_curl(); +static EVEL_ERR_CODES evel_send_to_another_collector(const EVEL_EVENT_DOMAINS evel_domain, char * json_body, size_t json_size); + +/**************************************************************************//** + * Buffers for error strings from libcurl. + *****************************************************************************/ +static char curl_err_string[CURL_ERROR_SIZE] = "<NULL>"; + +/**************************************************************************//** + * Handle for the API into libcurl. + *****************************************************************************/ +static CURL * curl_handle = NULL; +int curr_global_handles = 0; + +/**************************************************************************//** + * Special headers that we send. + *****************************************************************************/ +static struct curl_slist * hdr_chunk = NULL; + +/**************************************************************************//** + * Message queue for sending events to the API. + *****************************************************************************/ +static ring_buffer event_buffer; + +/**************************************************************************//** + * Single pending priority post, which can be generated as a result of a + * response to an event. Currently only used to respond to a commandList. + *****************************************************************************/ +static MEMORY_CHUNK priority_post; + +/**************************************************************************//** + * The thread which is responsible for handling events off of the ring-buffer + * and posting them to the Event Handler API. + *****************************************************************************/ +static pthread_t evt_handler_thread; + +/**************************************************************************//** + * Variable to convey to the event handler thread what the foreground wants it + * to do. + *****************************************************************************/ +static EVT_HANDLER_STATE evt_handler_state = EVT_HANDLER_UNINITIALIZED; + +/**************************************************************************//** + * The configured API URL for event and throttling. + *****************************************************************************/ +static char * evel_event_api_url; +static char * evel_throt_api_url; +static char * evel_batch_api_url; + +static char * evel_bevent_api_url; +static char * evel_bthrot_api_url; +static char * evel_bbatch_api_url; + +/**************************************************************************//** + * Storage for other CURL related parameters + *****************************************************************************/ +int evel_secure = -1; +int evel_verbosity = -1; + +long evel_verify_peer = 0; +long evel_verify_host = 0; + +static char * evel_source_ip = NULL; +static char * evel_source_ip_bakup = NULL; +static char * evel_cert_file_path = NULL; +static char * evel_key_file_path = NULL; +static char * evel_ca_info = NULL; +static char * evel_ca_file_path = NULL; +static char * evel_username = NULL; +static char * evel_password = NULL; +static char * evel_username2 = NULL; +static char * evel_password2 = NULL; + +static long http_response_code = 0; +static int evel_collector_id = 1; +/**************************************************************************//** + * Initialize the event handler. + * + * Primarily responsible for getting CURL ready for use. + * + * @param[in] event_api_url + * The URL where the Vendor Event Listener API is expected + * to be. + * @param[in] throt_api_url + * The URL where the Throttling API is expected to be. + * @param[in] source_ip Source IP of VES Agent + * @param[in] ring_buf_size Initial size of ring buffer + * @param[in] secure Whether Using http or https + * @param[in] cert_file_path Path to Client Certificate file + * @param[in] key_file_path Path to Client key file + * @param[in] ca_info Path to CA info file + * @param[in] ca_file_path Path to CA file + * @param[in] verify_peer Using peer verification or not 0 or 1 + * @param[in] verify_host Using host verification or not 0 or 1 + * @param[in] username The username for the Basic Authentication of requests. + * @param[in] password The password for the Basic Authentication of requests. + * @param verbosity 0 for normal operation, positive values for chattier + * logs. + *****************************************************************************/ +EVEL_ERR_CODES event_handler_initialize(const char * const event_api_url, + const char * const bakup_api_url, + const char * const throt_api_url, + const char * const source_ip, + const char * const source_ip_bakup, + int ring_buf_size, + int secure, + const char * const cert_file_path, + const char * const key_file_path, + const char * const ca_info, + const char * const ca_file_path, + long verify_peer, + long verify_host, + const char * const username, + const char * const password, + const char * const username2, + const char * const password2, + int verbosity) +{ + int rc = EVEL_SUCCESS; + char batch_api_url[EVEL_MAX_URL_LEN + 1] = {0}; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check assumptions. */ + /***************************************************************************/ + assert(event_api_url != NULL); + assert(throt_api_url != NULL); + assert(username != NULL); + assert(password != NULL); + if( bakup_api_url != NULL ) + { + assert(username2 != NULL); + assert(password2 != NULL); + } + + /***************************************************************************/ + /* Store the API URLs. */ + /***************************************************************************/ + evel_event_api_url = strdup(event_api_url); + assert(evel_event_api_url != NULL); + sprintf(batch_api_url,"%s/eventBatch",event_api_url); + evel_batch_api_url = strdup(batch_api_url); + assert(evel_batch_api_url != NULL); + evel_throt_api_url = strdup(throt_api_url); + assert(evel_throt_api_url != NULL); + + curr_global_handles = 1; + + if( bakup_api_url != NULL ) + { + evel_bevent_api_url = strdup(bakup_api_url); + assert(evel_bevent_api_url != NULL); + sprintf(batch_api_url,"%s/eventBatch",bakup_api_url); + evel_bbatch_api_url = strdup(batch_api_url); + assert(evel_bbatch_api_url != NULL); + evel_bthrot_api_url = strdup(throt_api_url); + assert(evel_bthrot_api_url != NULL); + curr_global_handles = 2; + } + + /***************************************************************************/ + /* Store other parameters */ + /***************************************************************************/ + evel_secure = secure; + evel_verbosity = verbosity; + + evel_verify_peer = verify_peer; + evel_verify_host = verify_host; + + evel_source_ip = NULL; + if (source_ip != NULL) + { + evel_source_ip = strdup(source_ip); + assert(evel_source_ip != NULL); + } + + evel_source_ip_bakup = NULL; + if (source_ip_bakup != NULL) + { + evel_source_ip_bakup = strdup(source_ip_bakup); + assert(evel_source_ip_bakup != NULL); + } + + evel_cert_file_path = NULL; + if (cert_file_path != NULL) + { + evel_cert_file_path = strdup(cert_file_path); + assert(evel_cert_file_path != NULL); + } + + evel_key_file_path = NULL; + if (key_file_path != NULL) + { + evel_key_file_path = strdup(key_file_path); + assert(evel_key_file_path != NULL); + } + + evel_ca_info = NULL; + if (ca_info != NULL) + { + evel_ca_info = strdup(ca_info); + assert(evel_ca_info != NULL); + } + + evel_ca_file_path = NULL; + if (ca_file_path != NULL) + { + evel_ca_file_path = strdup(ca_file_path); + assert(evel_ca_file_path != NULL); + } + + evel_username = NULL; + if (username != NULL) + { + evel_username = strdup(username); + assert(evel_username != NULL); + } + + evel_password = NULL; + if (password != NULL) + { + evel_password = strdup(password); + assert(evel_password != NULL); + } + + evel_username2 = NULL; + if (username2 != NULL) + { + evel_username2 = strdup(username2); + assert(evel_username2 != NULL); + } + + evel_password2 = NULL; + if (password2 != NULL) + { + evel_password2 = strdup(password2); + assert(evel_password2 != NULL); + } + + curl_version_info_data *d = curl_version_info(CURLVERSION_NOW); + /* compare with the 24 bit hex number in 8 bit fields */ + if(d->version_num >= 0x072100) { + /* this is libcurl 7.33.0 or later */ + EVEL_INFO("7.33 or later Curl version %x.",d->version_num); + } + else { + EVEL_INFO("Old Curl version."); + } + + /***************************************************************************/ + /* Initialize a message ring-buffer to be used between the foreground and */ + /* the thread which sends the messages. This can't fail. */ + /***************************************************************************/ + if( ring_buf_size < EVEL_EVENT_BUFFER_DEPTH ) + { + log_error_state("Warning: Failed to initialize Ring buffer size to %d. ", + ring_buf_size); + goto exit_label; + } + ring_buffer_initialize(&event_buffer, EVEL_EVENT_BUFFER_DEPTH); + +exit_label: + + EVEL_EXIT(); + + return rc; + +} +/**************************************************************************//** + * Setup the curl connection to collector + * + * Primarily responsible for getting CURL ready to send message. Also it would + * be used to swithch over to other collector + *****************************************************************************/ +static EVEL_ERR_CODES evel_setup_curl() +{ + int rc = EVEL_SUCCESS; + CURLcode curl_rc = CURLE_OK; + char local_address[64]; + char * api_url = NULL; + char * username = NULL; + char * password = NULL; + char * source_ip = NULL; + + EVEL_ENTER(); + + if (evel_collector_id > 2) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Wrong evel_collector- value > 2"); + goto exit_label; + } + + /***************************************************************************/ + /* Initialize the local variable with proper global variables that are */ + /* required to setup the connection */ + /***************************************************************************/ + if (evel_collector_id == 1) + { + api_url = evel_event_api_url; + source_ip = evel_source_ip; + username = evel_username; + password = evel_password; + } + else if (evel_collector_id == 2) + { + api_url = evel_bevent_api_url; + source_ip = evel_source_ip_bakup; + username = evel_username2; + password = evel_password2; + } + /***************************************************************************/ + /* Clean-up the cURL library. */ + /***************************************************************************/ + if (curl_handle != NULL) + { + curl_easy_cleanup(curl_handle); + curl_handle = NULL; + } + if (hdr_chunk != NULL) + { + curl_slist_free_all(hdr_chunk); + hdr_chunk = NULL; + } + + /***************************************************************************/ + /* Start the CURL library. Note that this initialization is not threadsafe */ + /* which imposes a constraint that the EVEL library is initialized before */ + /* any threads are started. */ + /***************************************************************************/ + curl_rc = curl_global_init(CURL_GLOBAL_SSL); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL. Error code=%d", curl_rc); + goto exit_label; + } + + /***************************************************************************/ + /* Get a curl handle which we'll use for all of our output. */ + /***************************************************************************/ + curl_handle = curl_easy_init(); + if (curl_handle == NULL) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to get libCURL handle"); + goto exit_label; + } + + /***************************************************************************/ + /* Prime the library to give friendly error codes. */ + /***************************************************************************/ + curl_rc = curl_easy_setopt(curl_handle, + CURLOPT_ERRORBUFFER, + curl_err_string); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL to provide friendly errors. " + "Error code=%d", curl_rc); + goto exit_label; + } + + /***************************************************************************/ + /* If running in verbose mode generate more output. */ + /***************************************************************************/ + if (evel_verbosity > 0) + { + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL to be verbose. " + "Error code=%d", curl_rc); + goto exit_label; + } + } + + /***************************************************************************/ + /* Set the URL for the API. */ + /***************************************************************************/ + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, api_url); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL with the API URL. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + EVEL_INFO("Initializing CURL to send events to: %s", api_url); + + /***************************************************************************/ + /* send all data to this function. */ + /***************************************************************************/ + curl_rc = curl_easy_setopt(curl_handle, + CURLOPT_WRITEFUNCTION, + evel_write_callback); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL with the write callback. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + + /***************************************************************************/ + /* configure local ip address if provided */ + /* Default ip if NULL */ + /***************************************************************************/ + if( source_ip != NULL ) + { + snprintf(local_address,sizeof(local_address),source_ip); + if( local_address[0] != '\0' ) + { + curl_rc = curl_easy_setopt(curl_handle, + CURLOPT_INTERFACE, + local_address); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL with the local address. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + } + } + + /***************************************************************************/ + /* configure SSL options for HTTPS transfers */ + /***************************************************************************/ + if( evel_secure ) + { + if( evel_cert_file_path != NULL ) + { + curl_rc = curl_easy_setopt(curl_handle, + CURLOPT_SSLCERT, + evel_cert_file_path); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL with the client cert. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + } + + if( evel_key_file_path != NULL ) + { + curl_rc = curl_easy_setopt(curl_handle, + CURLOPT_SSLKEY, + evel_key_file_path); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL with the client key. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + } + + if( evel_ca_info != NULL ) + { + curl_rc = curl_easy_setopt(curl_handle, + CURLOPT_CAINFO, + evel_ca_info); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL with the CA cert file. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + } + + if( evel_ca_file_path != NULL ) + { + curl_rc = curl_easy_setopt(curl_handle, + CURLOPT_CAPATH, + evel_ca_file_path); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL with the CA cert path. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + } + + curl_rc = curl_easy_setopt(curl_handle, + CURLOPT_SSL_VERIFYPEER, + evel_verify_peer); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL with SSL Server verification. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + curl_rc = curl_easy_setopt(curl_handle, + CURLOPT_SSL_VERIFYHOST, + evel_verify_host); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL with Client host verification. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + + } + + + + /***************************************************************************/ + /* some servers don't like requests that are made without a user-agent */ + /* field, so we provide one. */ + /***************************************************************************/ + curl_rc = curl_easy_setopt(curl_handle, + CURLOPT_USERAGENT, + "libcurl-agent/1.0"); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL to upload. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + + /***************************************************************************/ + /* Specify that we are going to POST data. */ + /***************************************************************************/ + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_POST, 1L); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL to upload. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + + /***************************************************************************/ + /* we want to use our own read function. */ + /***************************************************************************/ + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, read_callback); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL to upload using read " + "function. Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + + /***************************************************************************/ + /* All of our events are JSON encoded. We also suppress the */ + /* Expect: 100-continue header that we would otherwise get since it */ + /* confuses some servers. */ + /* */ + /* @TODO: do AT&T want this behavior? */ + /***************************************************************************/ + hdr_chunk = curl_slist_append(hdr_chunk, "Content-type: application/json"); + hdr_chunk = curl_slist_append(hdr_chunk, "Expect:"); + + /***************************************************************************/ + /* set our custom set of headers. */ + /***************************************************************************/ + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, hdr_chunk); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL to use custom headers. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + + /***************************************************************************/ + /* Set the timeout for the operation. */ + /***************************************************************************/ + curl_rc = curl_easy_setopt(curl_handle, + CURLOPT_TIMEOUT, + EVEL_API_TIMEOUT); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL for API timeout. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + + /***************************************************************************/ + /* Set that we want Basic authentication with username:password Base-64 */ + /* encoded for the operation. */ + /***************************************************************************/ + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL for Basic Authentication. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_USERNAME, username); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL with username. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_PASSWORD, password); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL with password. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + + /***************************************************************************/ + /* Initialize the priority post buffer to empty. */ + /***************************************************************************/ + priority_post.memory = NULL; + +exit_label: + EVEL_EXIT(); + + return(rc); +} + +/**************************************************************************//** + * Run the event handler. + * + * Spawns the thread responsible for handling events and sending them to the + * API. + * + * @return Status code. + * @retval ::EVEL_SUCCESS if everything OK. + * @retval One of ::EVEL_ERR_CODES if there was a problem. + *****************************************************************************/ +EVEL_ERR_CODES event_handler_run() +{ + EVEL_ERR_CODES rc = EVEL_SUCCESS; + int pthread_rc = 0; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Start the event handler thread. */ + /***************************************************************************/ + evt_handler_state = EVT_HANDLER_INACTIVE; + pthread_rc = pthread_create(&evt_handler_thread, NULL, event_handler, NULL); + if (pthread_rc != 0) + { + rc = EVEL_PTHREAD_LIBRARY_FAIL; + log_error_state("Failed to start event handler thread. " + "Error code=%d", pthread_rc); + } + + EVEL_EXIT() + return rc; +} + +/**************************************************************************//** + * Terminate the event handler. + * + * Shuts down the event handler thread in as clean a way as possible. Sets the + * global exit flag and then signals the thread to interrupt it since it's + * most likely waiting on the ring-buffer. + * + * Having achieved an orderly shutdown of the event handler thread, clean up + * the cURL library's resources cleanly. + * + * @return Status code. + * @retval ::EVEL_SUCCESS if everything OK. + * @retval One of ::EVEL_ERR_CODES if there was a problem. + *****************************************************************************/ +EVEL_ERR_CODES event_handler_terminate() +{ + EVEL_ERR_CODES rc = EVEL_SUCCESS; + + EVEL_ENTER(); + EVENT_INTERNAL *event = NULL; + + /***************************************************************************/ + /* Make sure that we were initialized before trying to terminate the */ + /* event handler thread. */ + /***************************************************************************/ + if (evt_handler_state != EVT_HANDLER_UNINITIALIZED) + { + /*************************************************************************/ + /* Make sure that the event handler knows it's time to die. */ + /*************************************************************************/ + event = evel_new_internal_event(EVT_CMD_TERMINATE,"EVELinternal","EVELid"); + if (event == NULL) + { + /***********************************************************************/ + /* We failed to get an event, but we don't bail out - we will just */ + /* clean up what we can and continue on our way, since we're exiting */ + /* anyway. */ + /***********************************************************************/ + EVEL_ERROR("Failed to get internal event - perform dirty exit instead!"); + } + else + { + /***********************************************************************/ + /* Post the event then wait for the Event Handler to exit. Set the */ + /* global command, too, in case the ring-buffer is full. */ + /***********************************************************************/ + EVEL_DEBUG("Sending event to Event Hander to request it to exit."); + evt_handler_state = EVT_HANDLER_REQUEST_TERMINATE; + evel_post_event((EVENT_HEADER *) event); + pthread_join(evt_handler_thread, NULL); + EVEL_DEBUG("Event Handler thread has exited."); + } + } + else + { + EVEL_DEBUG("Event handler was not initialized, so no need to kill it"); + } + + /***************************************************************************/ + /* Clean-up the cURL library. */ + /***************************************************************************/ + if (curl_handle != NULL) + { + curl_easy_cleanup(curl_handle); + curl_handle = NULL; + } + if (hdr_chunk != NULL) + { + curl_slist_free_all(hdr_chunk); + hdr_chunk = NULL; + } + + /***************************************************************************/ + /* Free off the stored API URL strings. */ + /***************************************************************************/ + if (evel_event_api_url != NULL) + { + free(evel_event_api_url); + evel_event_api_url = NULL; + } + if (evel_batch_api_url != NULL) + { + free(evel_batch_api_url); + evel_batch_api_url = NULL; + } + if (evel_throt_api_url != NULL) + { + free(evel_throt_api_url); + evel_throt_api_url = NULL; + } + + EVEL_EXIT(); + return rc; +} + +/**************************************************************************//** + * Post an event. + * + * @note So far as the caller is concerned, successfully posting the event + * relinquishes all responsibility for the event - the library will take care + * of freeing the event in due course. + + * @param event The event to be posted. + * + * @returns Status code + * @retval EVEL_SUCCESS On success + * @retval "One of ::EVEL_ERR_CODES" On failure. + *****************************************************************************/ +EVEL_ERR_CODES evel_post_event(EVENT_HEADER * event) +{ + int rc = EVEL_SUCCESS; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(event != NULL); + + /***************************************************************************/ + /* We need to make sure that we are either initializing or running */ + /* normally before writing the event into the buffer so that we can */ + /* guarantee that the ring-buffer empties properly on exit. */ + /***************************************************************************/ + if ((evt_handler_state == EVT_HANDLER_ACTIVE) || + (evt_handler_state == EVT_HANDLER_INACTIVE) || + (evt_handler_state == EVT_HANDLER_REQUEST_TERMINATE)) + { + if (ring_buffer_write(&event_buffer, event) == 0) + { + log_error_state("Failed to write event to buffer - event dropped!"); + rc = EVEL_EVENT_BUFFER_FULL; + evel_free_event(event); + } + } + else + { + /*************************************************************************/ + /* System is not in active operation, so reject the event. */ + /*************************************************************************/ + log_error_state("Event Handler system not active - event dropped!"); + rc = EVEL_EVENT_HANDLER_INACTIVE; + evel_free_event(event); + } + + EVEL_EXIT(); + return (rc); +} + +/**************************************************************************//** + * Post an event to the Vendor Event Listener API. + * + * @returns Status code + * @retval EVEL_SUCCESS On success + * @retval "One of ::EVEL_ERR_CODES" On failure. + *****************************************************************************/ +static EVEL_ERR_CODES evel_post_api(char * msg, size_t size) +{ + int rc = EVEL_SUCCESS; + CURLcode curl_rc = CURLE_OK; + MEMORY_CHUNK rx_chunk; + MEMORY_CHUNK tx_chunk; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Create the memory chunk to be used for the response to the post. The */ + /* will be realloced. */ + /***************************************************************************/ + rx_chunk.memory = malloc(1); + assert(rx_chunk.memory != NULL); + rx_chunk.size = 0; + + /***************************************************************************/ + /* Create the memory chunk to be sent as the body of the post. */ + /***************************************************************************/ + tx_chunk.memory = msg; + tx_chunk.size = size; + EVEL_DEBUG("Sending chunk of size %d", tx_chunk.size); + + /***************************************************************************/ + /* Point to the data to be received. */ + /***************************************************************************/ + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &rx_chunk); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL to upload. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + EVEL_DEBUG("Initialized data to receive"); + + /***************************************************************************/ + /* Pointer to pass to our read function */ + /***************************************************************************/ + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_READDATA, &tx_chunk); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to set upload data for libCURL to upload. " + "Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + EVEL_DEBUG("Initialized data to send"); + + /***************************************************************************/ + /* Size of the data to transmit. */ + /***************************************************************************/ + curl_rc = curl_easy_setopt(curl_handle, + CURLOPT_POSTFIELDSIZE, + tx_chunk.size); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to set length of upload data for libCURL to " + "upload. Error code=%d (%s)", curl_rc, curl_err_string); + goto exit_label; + } + EVEL_DEBUG("Initialized length of data to send"); + + /***************************************************************************/ + /* Now run off and do what you've been told! */ + /***************************************************************************/ + http_response_code = 0; + + curl_rc = curl_easy_perform(curl_handle); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to transfer an event to Vendor Event Listener! " + "Error code=%d (%s)", curl_rc, curl_err_string); + EVEL_ERROR("Dropped event: %s", msg); + goto exit_label; + } + + /***************************************************************************/ + /* See what response we got - any 2XX response is good. */ + /***************************************************************************/ + curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_response_code); + EVEL_DEBUG("HTTP response code: %d", http_response_code); + if ((http_response_code / 100) == 2) + { + /*************************************************************************/ + /* If the server responded with data it may be interesting but not a */ + /* problem. */ + /*************************************************************************/ + if ((rx_chunk.size > 0) && (rx_chunk.memory != NULL)) + { + EVEL_DEBUG("Server returned data = %d (%s)", + rx_chunk.size, + rx_chunk.memory); + + /***********************************************************************/ + /* If this is a response to priority post, then we're not interested. */ + /***********************************************************************/ + if (priority_post.memory != NULL) + { + EVEL_ERROR("Ignoring priority post response"); + } + else + { + evel_handle_event_response(&rx_chunk, &priority_post); + } + } + } + else + { + EVEL_ERROR("Unexpected HTTP response code: %d with data size %d (%s)", + http_response_code, + rx_chunk.size, + rx_chunk.size > 0 ? rx_chunk.memory : "NONE"); + EVEL_ERROR("Potentially dropped event: %s", msg); + } + +exit_label: + free(rx_chunk.memory); + EVEL_EXIT(); + return(rc); +} + +/**************************************************************************//** + * Send event to another collector + * + * Identify the next collector and try sending the event to that collector + ****************************************************************************/ +static EVEL_ERR_CODES evel_send_to_another_collector( + const EVEL_EVENT_DOMAINS evel_domain, + char * json_body, + size_t json_size) +{ + int rc = EVEL_SUCCESS; + CURLcode curl_rc; + + EVEL_ENTER(); + + if ((evel_collector_id == 1) && (curr_global_handles == 2)) + { + evel_collector_id =2; + } + else if (evel_collector_id == 2) + { + evel_collector_id =1; + } + + rc = evel_setup_curl(); + + if ( rc == EVEL_SUCCESS) + { + if (evel_collector_id == 1) + { + if (evel_domain == EVEL_DOMAIN_BATCH) + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_batch_api_url); + else + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_event_api_url); + } + else if (evel_collector_id == 2) + { + if (evel_domain == EVEL_DOMAIN_BATCH) + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_bbatch_api_url); + else + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_bevent_api_url); + } + + rc = evel_post_api(json_body, json_size); + } + + EVEL_EXIT(); + + return rc; +} + +/**************************************************************************//** + * Callback function to provide data to send. + * + * Copy data into the supplied buffer, read_callback::ptr, checking size + * limits. + * + * @returns Number of bytes placed into read_callback::ptr. 0 for EOF. + *****************************************************************************/ +static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp) +{ + size_t rtn = 0; + size_t bytes_to_write = 0; + MEMORY_CHUNK *tx_chunk = (MEMORY_CHUNK *)userp; + + EVEL_ENTER(); + + bytes_to_write = min(size*nmemb, tx_chunk->size); + + if (bytes_to_write > 0) + { + EVEL_DEBUG("Going to try to write %d bytes", bytes_to_write); + strncpy((char *)ptr, tx_chunk->memory, bytes_to_write); + tx_chunk->memory += bytes_to_write; + tx_chunk->size -= bytes_to_write; + rtn = bytes_to_write; + } + else + { + EVEL_DEBUG("Reached EOF"); + } + + EVEL_EXIT(); + return rtn; +} + +/**************************************************************************//** + * Callback function to provide returned data. + * + * Copy data into the supplied buffer, write_callback::ptr, checking size + * limits. + * + * @returns Number of bytes placed into write_callback::ptr. 0 for EOF. + *****************************************************************************/ +size_t evel_write_callback(void *contents, + size_t size, + size_t nmemb, + void *userp) +{ + size_t realsize = size * nmemb; + MEMORY_CHUNK * rx_chunk = (MEMORY_CHUNK *)userp; + + EVEL_ENTER(); + + EVEL_DEBUG("Called with %d chunks of %d size = %d", nmemb, size, realsize); + EVEL_DEBUG("rx chunk size is %d", rx_chunk->size); + + rx_chunk->memory = realloc(rx_chunk->memory, rx_chunk->size + realsize + 1); + if(rx_chunk->memory == NULL) { + /* out of memory! */ + printf("not enough memory (realloc returned NULL)\n"); + return 0; + } + + memcpy(&(rx_chunk->memory[rx_chunk->size]), contents, realsize); + rx_chunk->size += realsize; + rx_chunk->memory[rx_chunk->size] = 0; + + EVEL_DEBUG("Rx data: %s", rx_chunk->memory); + EVEL_DEBUG("Returning: %d", realsize); + + EVEL_EXIT(); + return realsize; +} + +/**************************************************************************//** + * Event Handler. + * + * Watch for messages coming on the internal queue and send them to the + * listener. + * + * param[in] arg Argument - unused. + *****************************************************************************/ +static void * event_handler(void * arg __attribute__ ((unused))) +{ + int old_type = 0; + EVENT_HEADER * msg = NULL; + EVENT_INTERNAL * internal_msg = NULL; + int json_size = 0; + char json_body[EVEL_MAX_JSON_BODY]; + int rc = EVEL_SUCCESS; + CURLcode curl_rc; + int collector_down_count = 0; + int switch_coll = 0; + + EVEL_INFO("Event handler thread started"); + + /***************************************************************************/ + /* Set this thread to be cancellable immediately. */ + /***************************************************************************/ + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type); + + /***************************************************************************/ + /* Set the handler as active, defending against weird situations like */ + /* immediately shutting down after initializing the library so the */ + /* handler never gets started up properly. */ + /***************************************************************************/ + if (evt_handler_state == EVT_HANDLER_INACTIVE) + { + evt_handler_state = EVT_HANDLER_ACTIVE; + } + else + { + EVEL_ERROR("Event Handler State was not INACTIVE at start-up - " + "Handler will exit immediately!"); + } + /***************************************************************************/ + /* Set the connection to collector */ + /***************************************************************************/ + while (true) + { + evel_collector_id = 1; + rc = evel_setup_curl(); + + if ( rc != EVEL_SUCCESS) + { + EVEL_ERROR("Failed to setup the first collector. Error code=%d", rc); + if (curr_global_handles == 2) + { + EVEL_DEBUG("Switching to other collector"); + + evel_collector_id = 2; + + rc = evel_setup_curl(); + if ( rc != EVEL_SUCCESS) + { + EVEL_ERROR("Failed to setup the connection to second collector also, Error code%d", rc); + sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); + collector_down_count = collector_down_count + 1; + EVEL_ERROR("Collectors setup issue- retry count=%d", collector_down_count); + } + else + { + collector_down_count = 0; + break; + } + } + else + { + sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); + collector_down_count = collector_down_count + 1; + EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); + } + } + else + { + collector_down_count = 0; + break; + } + } + + while (evt_handler_state == EVT_HANDLER_ACTIVE) + { + /*************************************************************************/ + /* Wait for a message to be received. */ + /*************************************************************************/ + EVEL_DEBUG("Event handler getting any messages"); + msg = ring_buffer_read(&event_buffer); + + /*************************************************************************/ + /* Internal events get special treatment while regular events get posted */ + /* to the far side. */ + /*************************************************************************/ + if (msg->event_domain == EVEL_DOMAIN_BATCH ) + { + EVEL_DEBUG("Batch event received"); + + /***********************************************************************/ + /* Encode the event in JSON. */ + /***********************************************************************/ + json_size = evel_json_encode_batch_event(json_body, EVEL_MAX_JSON_BODY, msg); + + /***************************************************************************/ + /* Set the URL for the API. */ + /***************************************************************************/ + if (evel_collector_id == 1) + { + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_batch_api_url); + } + else + { + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_bbatch_api_url); + } + + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL with the Batch API URL. " + "Error code=%d (%s)", curl_rc, curl_err_string); + } + + /***********************************************************************/ + /* Send the JSON across the API. */ + /***********************************************************************/ + EVEL_DEBUG("Sending Batch JSON of size %d is: %s", json_size, json_body); + rc = evel_post_api(json_body, json_size); + + switch_coll = 0; + if ((rc == EVEL_SUCCESS) && ((http_response_code / 100) != 2)) + { + switch_coll = 1; + if (http_response_code == 400) // 400 - Bad JSON related return code + switch_coll = 0; + } + + if ((rc != EVEL_SUCCESS) || (switch_coll == 1)) + { + EVEL_ERROR("Failed to transfer the data. Error code=%d", rc); + EVEL_DEBUG("Switching to other collector if any"); + + while (true) + { + if (curr_global_handles == 2) + { + rc = evel_send_to_another_collector(msg->event_domain, json_body, json_size); + + switch_coll = 0; + if ((rc == EVEL_SUCCESS) && ((http_response_code / 100) != 2)) + { + switch_coll = 1; + if (http_response_code == 400) // 400 - Bad JSON related return code + switch_coll = 0; + } + if ((rc != EVEL_SUCCESS) || (switch_coll == 1)) + { + sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); + collector_down_count = collector_down_count + 1; + EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); + } + else + { + break; + } + } + else + { + sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); + collector_down_count = collector_down_count + 1; + EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); + } + + rc = evel_send_to_another_collector(msg->event_domain, json_body, json_size); + + switch_coll = 0; + if ((rc == EVEL_SUCCESS) && ((http_response_code / 100) != 2)) + { + switch_coll = 1; + if (http_response_code == 400) // 400 - Bad JSON related return code + switch_coll = 0; + } + if ((rc != EVEL_SUCCESS) || (switch_coll == 1)) + { + collector_down_count = collector_down_count + 1; + EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); + } + else + { + break; + } + } + } + } + else if (msg->event_domain != EVEL_DOMAIN_INTERNAL ) + { + EVEL_DEBUG("External event received"); + + /***********************************************************************/ + /* Encode the event in JSON. */ + /***********************************************************************/ + json_size = evel_json_encode_event(json_body, EVEL_MAX_JSON_BODY, msg); + + /***************************************************************************/ + /* Set the URL for the API. */ + /***************************************************************************/ + if (evel_collector_id == 1) + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_event_api_url); + else + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_bevent_api_url); + if (curl_rc != CURLE_OK) + { + rc = EVEL_CURL_LIBRARY_FAIL; + log_error_state("Failed to initialize libCURL with the API URL. " + "Error code=%d (%s)", curl_rc, curl_err_string); + } + + /***********************************************************************/ + /* Send the JSON across the API. */ + /***********************************************************************/ + EVEL_DEBUG("Sending JSON of size %d is: %s", json_size, json_body); + rc = evel_post_api(json_body, json_size); + + switch_coll = 0; + if ((rc == EVEL_SUCCESS) && ((http_response_code / 100) != 2)) + { + switch_coll = 1; + if (http_response_code == 400) // 400 - Bad JSON related return code + switch_coll = 0; + } + + if ((rc != EVEL_SUCCESS) || (switch_coll == 1)) + { + EVEL_ERROR("Failed to transfer the data. Error code=%d", rc); + EVEL_DEBUG("Switching to other collector if any"); + + while (true) + { + if (curr_global_handles == 2) + { + rc = evel_send_to_another_collector(msg->event_domain, json_body, json_size); + + switch_coll = 0; + if ((rc == EVEL_SUCCESS) && ((http_response_code / 100) != 2)) + { + switch_coll = 1; + if (http_response_code == 400) // 400 - Bad JSON related return code + switch_coll = 0; + } + if ((rc != EVEL_SUCCESS) || (switch_coll == 1)) + { + sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); + collector_down_count = collector_down_count + 1; + EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); + } + else + { + break; + } + } + else + { + sleep(EVEL_COLLECTOR_RECONNECTION_WAIT_TIME); + collector_down_count = collector_down_count + 1; + EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); + } + + rc = evel_send_to_another_collector(msg->event_domain, json_body, json_size); + + switch_coll = 0; + if ((rc == EVEL_SUCCESS) && ((http_response_code / 100) != 2)) + { + switch_coll = 1; + if (http_response_code == 400) // 400 - Bad JSON related return code + switch_coll = 0; + } + if ((rc != EVEL_SUCCESS) || (switch_coll == 1)) + { + collector_down_count = collector_down_count + 1; + EVEL_ERROR("Collector setup issue-retry count=%d", collector_down_count); + } + else + { + break; + } + } + } + } + else + { + EVEL_DEBUG("Internal event received"); + internal_msg = (EVENT_INTERNAL *) msg; + assert(internal_msg->command == EVT_CMD_TERMINATE); + evt_handler_state = EVT_HANDLER_TERMINATING; + } + + /*************************************************************************/ + /* We are responsible for freeing the memory. */ + /*************************************************************************/ + evel_free_event(msg); + msg = NULL; + + /*************************************************************************/ + /* There may be a single priority post to be sent. */ + /*************************************************************************/ + if (priority_post.memory != NULL) + { + EVEL_DEBUG("Priority Post"); + + /***********************************************************************/ + /* Set the URL for the throttling API. */ + /***********************************************************************/ + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_throt_api_url); + if (curl_rc != CURLE_OK) + { + /*********************************************************************/ + /* This is only likely to happen with CURLE_OUT_OF_MEMORY, in which */ + /* case we carry on regardless. */ + /*********************************************************************/ + EVEL_ERROR("Failed to set throttling URL. Error code=%d", rc); + } + else + { + rc = evel_post_api(priority_post.memory, priority_post.size); + if (rc != EVEL_SUCCESS) + { + EVEL_ERROR("Failed to transfer priority post. Error code=%d", rc); + } + } + + /***********************************************************************/ + /* Reinstate the URL for the event API. */ + /***********************************************************************/ + curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_event_api_url); + if (curl_rc != CURLE_OK) + { + /*********************************************************************/ + /* This is only likely to happen with CURLE_OUT_OF_MEMORY, in which */ + /* case we carry on regardless. */ + /*********************************************************************/ + EVEL_ERROR("Failed to reinstate events URL. Error code=%d", rc); + } + + /***********************************************************************/ + /* We are responsible for freeing the memory. */ + /***********************************************************************/ + free(priority_post.memory); + priority_post.memory = NULL; + } + } + + /***************************************************************************/ + /* The event handler is now exiting. The ring-buffer could contain events */ + /* which have not been processed, so deplete those. Because we've been */ + /* asked to exit we can be confident that the foreground will have stopped */ + /* sending events in so we know that this process will conclude! */ + /***************************************************************************/ + evt_handler_state = EVT_HANDLER_TERMINATING; + while (!ring_buffer_is_empty(&event_buffer)) + { + EVEL_DEBUG("Reading event from buffer"); + msg = ring_buffer_read(&event_buffer); + evel_free_event(msg); + } + evt_handler_state = EVT_HANDLER_TERMINATED; + EVEL_INFO("Event handler thread stopped"); + + return (NULL); +} + +/**************************************************************************//** + * Handle a JSON response from the listener, contained in a ::MEMORY_CHUNK. + * + * Tokenize the response, and decode any tokens found. + * + * @param chunk The memory chunk containing the response. + * @param post The memory chunk in which to place any resulting POST. + *****************************************************************************/ +void evel_handle_event_response(const MEMORY_CHUNK * const chunk, + MEMORY_CHUNK * const post) +{ + jsmn_parser json_parser; + jsmntok_t json_tokens[EVEL_MAX_RESPONSE_TOKENS]; + int num_tokens = 0; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(chunk != NULL); + assert(priority_post.memory == NULL); + + EVEL_DEBUG("Response size = %d", chunk->size); + EVEL_DEBUG("Response = %s", chunk->memory); + + /***************************************************************************/ + /* Initialize the parser and tokenize the response. */ + /***************************************************************************/ + jsmn_init(&json_parser); + num_tokens = jsmn_parse(&json_parser, + chunk->memory, + chunk->size, + json_tokens, + EVEL_MAX_RESPONSE_TOKENS); + + if (num_tokens < 0) + { + EVEL_ERROR("Failed to parse JSON response. " + "Error code=%d", num_tokens); + } + else if (num_tokens == 0) + { + EVEL_DEBUG("No tokens found in JSON response"); + } + else + { + EVEL_DEBUG("Decode JSON response tokens"); + if (!evel_handle_response_tokens(chunk, json_tokens, num_tokens, post)) + { + EVEL_ERROR("Failed to handle JSON response."); + } + } + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Handle a JSON response from the listener, as a list of tokens from JSMN. + * + * @param chunk Memory chunk containing the JSON buffer. + * @param json_tokens Array of tokens to handle. + * @param num_tokens The number of tokens to handle. + * @param post The memory chunk in which to place any resulting POST. + * @return true if we handled the response, false otherwise. + *****************************************************************************/ +bool evel_handle_response_tokens(const MEMORY_CHUNK * const chunk, + const jsmntok_t * const json_tokens, + const int num_tokens, + MEMORY_CHUNK * const post) +{ + bool json_ok = true; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(chunk != NULL); + assert(json_tokens != NULL); + assert(num_tokens < EVEL_MAX_RESPONSE_TOKENS); + + /***************************************************************************/ + /* Peek at the tokens to decide what the response it, then call the */ + /* appropriate handler to handle it. There is only one handler at this */ + /* point. */ + /***************************************************************************/ + if (evel_tokens_match_command_list(chunk, json_tokens, num_tokens)) + { + json_ok = evel_handle_command_list(chunk, json_tokens, num_tokens, post); + } + + EVEL_EXIT(); + + return json_ok; +} + +/**************************************************************************//** + * Determine whether a list of tokens looks like a "commandList" response. + * + * @param chunk Memory chunk containing the JSON buffer. + * @param json_tokens Token to check. + * @param num_tokens The number of tokens to handle. + * @return true if the tokens look like a "commandList" match, or false. + *****************************************************************************/ +bool evel_tokens_match_command_list(const MEMORY_CHUNK * const chunk, + const jsmntok_t * const json_tokens, + const int num_tokens) +{ + bool result = false; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Make some checks on the basic layout of the commandList. */ + /***************************************************************************/ + if ((num_tokens > 3) && + (json_tokens[0].type == JSMN_OBJECT) && + (json_tokens[1].type == JSMN_STRING) && + (json_tokens[2].type == JSMN_ARRAY) && + (evel_token_equals_string(chunk, &json_tokens[1], "commandList"))) + { + result = true; + } + + EVEL_EXIT(); + + return result; +} + +/**************************************************************************//** + * Check that a string token matches a given input string. + * + * @param chunk Memory chunk containing the JSON buffer. + * @param json_token Token to check. + * @param check_string String to check it against. + * @return true if the strings match, or false. + *****************************************************************************/ +bool evel_token_equals_string(const MEMORY_CHUNK * const chunk, + const jsmntok_t * json_token, + const char * check_string) +{ + bool result = false; + + EVEL_ENTER(); + + const int token_length = json_token->end - json_token->start; + const char * const token_string = chunk->memory + json_token->start; + + if (token_length == (int)strlen(check_string)) + { + result = (strncmp(token_string, check_string, token_length) == 0); + } + + EVEL_EXIT(); + + return result; +} |