diff options
author | 2020-04-27 21:53:00 +0000 | |
---|---|---|
committer | 2020-04-28 14:52:48 +0000 | |
commit | 08eddb8df44beacbb7b4047e313a771292030ccc (patch) | |
tree | ede637e3c6725bfd999d115b6ad330f1caa2602f /vnfs/VESreporting_vFW5.0_DANOS/evel/evel-library/code/evel_library/evel_throttle.c | |
parent | 6610dc1180d0dfbe30a737306d0f059b463aeb26 (diff) |
Initial VES for DANOS vRouter
1. Create vpp_measurement_reporter that use DANOS configd query
to retrieve statistics and libevel to transmit
2. Copy VES evel library to build a debian package under
vpp_measurement_report
3. Add debian files to create a vpp_measurement_reporter + libevel
debian package
4. Add debian install and systemctl start files
5. Add instruction to build VES reporter debian package (that will
include libevel.so)
6. Add instructions to build DANOS ISO with VES reporter debian
package and creating glance image
Issue-ID: INT-1566
Change-Id: If18f16525f07f1b6bae0fc105e0452263b4bf661
Signed-off-by: Brian Freeman <bf1936@att.com>
Diffstat (limited to 'vnfs/VESreporting_vFW5.0_DANOS/evel/evel-library/code/evel_library/evel_throttle.c')
-rw-r--r-- | vnfs/VESreporting_vFW5.0_DANOS/evel/evel-library/code/evel_library/evel_throttle.c | 2105 |
1 files changed, 2105 insertions, 0 deletions
diff --git a/vnfs/VESreporting_vFW5.0_DANOS/evel/evel-library/code/evel_library/evel_throttle.c b/vnfs/VESreporting_vFW5.0_DANOS/evel/evel-library/code/evel_library/evel_throttle.c new file mode 100644 index 00000000..ac664a17 --- /dev/null +++ b/vnfs/VESreporting_vFW5.0_DANOS/evel/evel-library/code/evel_library/evel_throttle.c @@ -0,0 +1,2105 @@ +/*************************************************************************//** + * + * 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. + * + ****************************************************************************/ + +#define _GNU_SOURCE +#include <string.h> +#include <assert.h> +#include <stdlib.h> +#include <limits.h> +#include <pthread.h> +#include <search.h> + +#include "evel_throttle.h" + +/*****************************************************************************/ +/* The Event Throttling State for all domains, indexed by */ +/* ::EVEL_EVENT_DOMAINS, corresponding to JSON eventDomain. */ +/* */ +/* A given domain is in a throttled state if ::evel_throttle_spec is */ +/* non-NULL. */ +/*****************************************************************************/ +static EVEL_THROTTLE_SPEC * evel_throttle_spec[EVEL_MAX_DOMAINS]; + +/*****************************************************************************/ +/* The current measurement interval. Default: MEASUREMENT_INTERVAL_UKNOWN. */ +/* Must be protected by evel_measurement_interval_mutex. */ +/*****************************************************************************/ +static int evel_measurement_interval; + +/*****************************************************************************/ +/* Mutex protecting evel_measurement_interval from contention between an */ +/* EVEL client reading it, and the EVEL event handler updating it. */ +/*****************************************************************************/ +static pthread_mutex_t evel_measurement_interval_mutex; + +/*****************************************************************************/ +/* Flag stating that we have received a "provideThrottlingState" command. */ +/* Set during JSON processing and cleared on sending the throttling state. */ +/*****************************************************************************/ +static bool evel_provide_throttling_state; + +/*****************************************************************************/ +/* Holder for the "commandType" value during JSON processing. */ +/*****************************************************************************/ +static char * evel_command_type_value; + +/*****************************************************************************/ +/* Holder for the "measurementInterval" value during JSON processing. */ +/*****************************************************************************/ +static char * evel_measurement_interval_value; + +/*****************************************************************************/ +/* Holder for the "eventDomain" value during JSON processing. */ +/*****************************************************************************/ +static char * evel_throttle_spec_domain_value; + +/*****************************************************************************/ +/* Decoded version of ::evel_throttle_spec_domain_value. */ +/*****************************************************************************/ +static EVEL_EVENT_DOMAINS evel_throttle_spec_domain; + +/*****************************************************************************/ +/* During JSON processing of a single throttling specification, we collect */ +/* parameters in this working ::EVEL_THROTTLE_SPEC */ +/*****************************************************************************/ +static EVEL_THROTTLE_SPEC * evel_temp_throttle; + +/*****************************************************************************/ +/* State tracking our progress through the command list */ +/*****************************************************************************/ +EVEL_JSON_COMMAND_STATE evel_json_command_state; + +/*****************************************************************************/ +/* Debug strings for ::EVEL_JSON_COMMAND_STATE. */ +/*****************************************************************************/ +static const char * const evel_jcs_strings[EVEL_JCS_MAX] = { + "EVEL_JCS_START", + "EVEL_JCS_COMMAND_LIST", + "EVEL_JCS_COMMAND_LIST_ENTRY", + "EVEL_JCS_COMMAND", + "EVEL_JCS_SPEC", + "EVEL_JCS_FIELD_NAMES", + "EVEL_JCS_PAIRS_LIST", + "EVEL_JCS_PAIRS_LIST_ENTRY", + "EVEL_JCS_NV_PAIR_NAMES" +}; + +/*****************************************************************************/ +/* Debug strings for JSON token type. */ +/*****************************************************************************/ +#define JSON_TOKEN_TYPES (JSMN_PRIMITIVE + 1) +static const char * const evel_json_token_strings[JSON_TOKEN_TYPES] = { + "JSMN_UNDEFINED", + "JSMN_OBJECT", + "JSMN_ARRAY", + "JSMN_STRING", + "JSMN_PRIMITIVE" +}; + +/*****************************************************************************/ +/* Debug strings for JSON domains. */ +/*****************************************************************************/ +static const char * evel_domain_strings[EVEL_MAX_DOMAINS] = { + "internal", + "batch", + "heartbeat", + "fault", + "measurementsForVfScaling", + "mobileFlow", + "report", + "serviceEvents", + "signaling", + "stateChange", + "syslog", + "other", + "voiceQuality", + "maxDomain" +}; + +/*****************************************************************************/ +/* Local prototypes. */ +/*****************************************************************************/ +static void evel_throttle_finalize(EVEL_THROTTLE_SPEC * throttle_spec); +static struct hsearch_data * evel_throttle_hash_create(DLIST * hash_keys); +static void evel_throttle_free(EVEL_THROTTLE_SPEC * throttle_spec); +static void evel_throttle_free_nv_pair(EVEL_SUPPRESSED_NV_PAIRS * nv_pairs); +static void evel_init_json_stack(EVEL_JSON_STACK * json_stack, + const MEMORY_CHUNK * const chunk); +static bool evel_stack_push(EVEL_JSON_STACK * const json_stack, + const int num_required, + const EVEL_JSON_STATE new_state); +static void evel_stack_pop(EVEL_JSON_STACK * const json_stack); +static void evel_stack_cleanup(EVEL_JSON_STACK * const json_stack); +static char * evel_stack_strdup(const MEMORY_CHUNK * const chunk, + const jsmntok_t * const token); +static void evel_stack_store_key(EVEL_JSON_STACK * const json_stack, + const jsmntok_t * const token); +static void evel_stack_store_value(EVEL_JSON_STACK * const json_stack, + const jsmntok_t * const token); +static void evel_stack_store_item(EVEL_JSON_STACK * const json_stack, + const jsmntok_t * const token); +static void evel_set_command_state(const EVEL_JSON_COMMAND_STATE new_state); +static void evel_debug_token(const MEMORY_CHUNK * const chunk, + const jsmntok_t * const token); +static void evel_command_list_response(MEMORY_CHUNK * const post); +static int evel_json_encode_throttle(char * const json, const int max_size); +static int evel_json_encode_throttle_spec(char * const json, + const int max_size, + const EVEL_EVENT_DOMAINS domain); +static int evel_json_encode_nv_pairs(char * const json, + const int max_size, + EVEL_SUPPRESSED_NV_PAIRS * nv_pairs); +static void evel_close_command(); +static void evel_open_command(); +static void evel_set_throttling_spec(); +static void evel_set_measurement_interval(); +static void evel_open_throttle_spec(); +static void evel_close_throttle_spec(); +static EVEL_EVENT_DOMAINS evel_decode_domain(char * domain_value); +static void evel_open_nv_pairs_list_entry(); +static void evel_close_nv_pairs_list_entry(); +static void evel_store_nv_pair_field_name(char * const value); +static void evel_store_nv_pair_name(char * const item); +static void evel_store_suppressed_field_name(char * const item); +static EVEL_SUPPRESSED_NV_PAIRS * evel_get_last_nv_pairs(); + +/**************************************************************************//** + * Return the current measurement interval provided by the Event Listener. + * + * @returns The current measurement interval + * @retval EVEL_MEASUREMENT_INTERVAL_UKNOWN (0) - interval has not been + * specified + *****************************************************************************/ +int evel_get_measurement_interval() +{ + int result; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Lock, read, unlock. */ + /***************************************************************************/ + pthread_mutex_lock(&evel_measurement_interval_mutex); + result = evel_measurement_interval; + pthread_mutex_unlock(&evel_measurement_interval_mutex); + + EVEL_EXIT(); + + return result; +} + +/**************************************************************************//** + * Return the ::EVEL_THROTTLE_SPEC for a given domain. + * + * @param domain The domain for which to return state. + *****************************************************************************/ +EVEL_THROTTLE_SPEC * evel_get_throttle_spec(EVEL_EVENT_DOMAINS domain) +{ + EVEL_THROTTLE_SPEC * result; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(domain < EVEL_MAX_DOMAINS); + + result = evel_throttle_spec[domain]; + + EVEL_EXIT(); + + return result; +} + +/**************************************************************************//** + * Determine whether a field_name should be suppressed. + * + * @param throttle_spec Throttle specification for the domain being encoded. + * @param field_name The field name to encoded or suppress. + * @return true if the field_name should be suppressed, false otherwise. + *****************************************************************************/ +bool evel_throttle_suppress_field(EVEL_THROTTLE_SPEC * throttle_spec, + const char * const field_name) +{ + bool suppress = false; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(field_name != NULL); + + /***************************************************************************/ + /* If the throttle spec and hash table exist, query the field_names table. */ + /***************************************************************************/ + if ((throttle_spec != NULL) && (throttle_spec->hash_field_names != NULL)) + { + ENTRY hash_query; + ENTRY * hash_result; + hash_query.key = (char * const) field_name; + suppress = (hsearch_r(hash_query, + FIND, + &hash_result, + throttle_spec->hash_field_names) != 0); + } + + EVEL_EXIT(); + + return suppress; +} + +/**************************************************************************//** + * Determine whether a name-value pair should be allowed (not suppressed). + * + * @param throttle_spec Throttle specification for the domain being encoded. + * @param field_name The field name holding the name-value pairs. + * @param name The name of the name-value pair to encoded or suppress. + * @return true if the name-value pair should be suppressed, false otherwise. + *****************************************************************************/ +bool evel_throttle_suppress_nv_pair(EVEL_THROTTLE_SPEC * throttle_spec, + const char * const field_name, + const char * const name) +{ + EVEL_SUPPRESSED_NV_PAIRS * nv_pairs; + bool hit = false; + bool suppress = false; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(field_name != NULL); + assert(name != NULL); + + /***************************************************************************/ + /* If the throttle spec and hash table exist, query the nv_pairs table. */ + /***************************************************************************/ + if ((throttle_spec != NULL) && (throttle_spec->hash_nv_pairs_list != NULL)) + { + ENTRY hash_query; + ENTRY * hash_result; + hash_query.key = (char * const) field_name; + hit = (hsearch_r(hash_query, + FIND, + &hash_result, + throttle_spec->hash_nv_pairs_list) != 0); + if (hit) + { + nv_pairs = hash_result->data; + } + } + + /***************************************************************************/ + /* If we got a hit, and the nv_pairs and hash table exist, query the */ + /* nv_pairs table. */ + /***************************************************************************/ + if (hit && (nv_pairs != NULL) && (nv_pairs->hash_nv_pair_names != NULL)) + { + ENTRY hash_query; + ENTRY * hash_result; + hash_query.key = (char * const) name; + suppress = (hsearch_r(hash_query, + FIND, + &hash_result, + nv_pairs->hash_nv_pair_names) != 0); + } + + EVEL_EXIT(); + + return suppress; +} + +/**************************************************************************//** + * Initialize event throttling to the default state. + * + * Called from ::evel_initialize. + *****************************************************************************/ +void evel_throttle_initialize() +{ + int pthread_rc; + int ii; + + EVEL_ENTER(); + + for (ii = 0; ii < EVEL_MAX_DOMAINS; ii++) + { + evel_throttle_spec[ii] = NULL; + } + + pthread_rc = pthread_mutex_init(&evel_measurement_interval_mutex, NULL); + assert(pthread_rc == 0); + + evel_measurement_interval = EVEL_MEASUREMENT_INTERVAL_UKNOWN; + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Clean up event throttling. + * + * Called from ::evel_terminate. + *****************************************************************************/ +void evel_throttle_terminate() +{ + int pthread_rc; + int ii; + + EVEL_ENTER(); + + for (ii = 0; ii < EVEL_MAX_DOMAINS; ii++) + { + if (evel_throttle_spec[ii] != NULL) + { + evel_throttle_free(evel_throttle_spec[ii]); + evel_throttle_spec[ii] = NULL; + } + } + + pthread_rc = pthread_mutex_destroy(&evel_measurement_interval_mutex); + assert(pthread_rc == 0); + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Finalize a single ::EVEL_THROTTLE_SPEC. + * + * Now that the specification is collected, build hash tables to simplify the + * throttling itself. + * + * @param throttle_spec The ::EVEL_THROTTLE_SPEC to finalize. + *****************************************************************************/ +void evel_throttle_finalize(EVEL_THROTTLE_SPEC * throttle_spec) +{ + int nv_pairs_count; + DLIST_ITEM * dlist_item; + ENTRY * add_result; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(throttle_spec != NULL); + + /***************************************************************************/ + /* Populate the hash table for suppressed field names. */ + /***************************************************************************/ + throttle_spec->hash_field_names = + evel_throttle_hash_create(&throttle_spec->suppressed_field_names); + + /***************************************************************************/ + /* Create the hash table for suppressed nv pairs. */ + /***************************************************************************/ + nv_pairs_count = dlist_count(&throttle_spec->suppressed_nv_pairs_list); + if (nv_pairs_count > 0) + { + throttle_spec->hash_nv_pairs_list = calloc(1, sizeof(struct hsearch_data)); + assert(throttle_spec->hash_nv_pairs_list != NULL); + + /*************************************************************************/ + /* Provide plenty of space in the table - see hcreate_r notes. */ + /*************************************************************************/ + if (hcreate_r(nv_pairs_count * 2, throttle_spec->hash_nv_pairs_list) == 0) + { + EVEL_ERROR("Failed to create hash table"); + free(throttle_spec->hash_nv_pairs_list); + throttle_spec->hash_nv_pairs_list = NULL; + } + } + + /***************************************************************************/ + /* Populate the hash tables under suppressed field names. */ + /***************************************************************************/ + dlist_item = dlist_get_first(&throttle_spec->suppressed_nv_pairs_list); + while (dlist_item != NULL) + { + EVEL_SUPPRESSED_NV_PAIRS * nv_pairs = dlist_item->item; + ENTRY hash_add; + + /*************************************************************************/ + /* Set the key to the string, and the item to the nv_pairs. */ + /*************************************************************************/ + assert(nv_pairs != NULL); + hash_add.key = nv_pairs->nv_pair_field_name; + hash_add.data = nv_pairs; + hsearch_r(hash_add, ENTER, &add_result, throttle_spec->hash_nv_pairs_list); + + /*************************************************************************/ + /* Create the nv_pair_names hash since we're in here. */ + /*************************************************************************/ + nv_pairs->hash_nv_pair_names = + evel_throttle_hash_create(&nv_pairs->suppressed_nv_pair_names); + + dlist_item = dlist_get_next(dlist_item); + } + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Create and populate a hash table from a DLIST of keys. + * + * @param hash_keys Pointer to a DLIST of hash table keys. + * @return Pointer to the created hash-table, or NULL on failure. + *****************************************************************************/ +struct hsearch_data * evel_throttle_hash_create(DLIST * hash_keys) +{ + int key_count; + struct hsearch_data * hash_table; + ENTRY * add_result; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(hash_keys != NULL); + + /***************************************************************************/ + /* Count the keys and if there are any, populate the hash table with them. */ + /***************************************************************************/ + key_count = dlist_count(hash_keys); + if (key_count > 0) + { + EVEL_DEBUG("Populating table for %d keys", key_count); + + hash_table = calloc(1, sizeof(struct hsearch_data)); + assert(hash_table != NULL); + + /*************************************************************************/ + /* We need to leave plenty of space in the table - see hcreate_r notes. */ + /*************************************************************************/ + if (hcreate_r(key_count * 2, hash_table) != 0) + { + DLIST_ITEM * dlist_item; + dlist_item = dlist_get_first(hash_keys); + while (dlist_item != NULL) + { + assert(dlist_item->item != NULL); + + /*********************************************************************/ + /* Set the key and data to the item, which is a string in this case. */ + /*********************************************************************/ + ENTRY hash_add; + hash_add.key = dlist_item->item; + hash_add.data = dlist_item->item; + hsearch_r(hash_add, ENTER, &add_result, hash_table); + dlist_item = dlist_get_next(dlist_item); + } + } + else + { + EVEL_ERROR("Failed to create hash table"); + free(hash_table); + hash_table = NULL; + } + } + else + { + hash_table = NULL; + } + + EVEL_EXIT(); + + return hash_table; +} + +/**************************************************************************//** + * Free resources associated with a single ::EVEL_THROTTLE_SPEC. + * + * @param throttle_spec The ::EVEL_THROTTLE_SPEC to free. + *****************************************************************************/ +void evel_throttle_free(EVEL_THROTTLE_SPEC * throttle_spec) +{ + char * field_name; + EVEL_SUPPRESSED_NV_PAIRS * nv_pairs; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(throttle_spec != NULL); + + /***************************************************************************/ + /* Free any hash tables. */ + /***************************************************************************/ + if (throttle_spec->hash_field_names != NULL) + { + hdestroy_r(throttle_spec->hash_field_names); + free(throttle_spec->hash_field_names); + } + if (throttle_spec->hash_nv_pairs_list != NULL) + { + hdestroy_r(throttle_spec->hash_nv_pairs_list); + free(throttle_spec->hash_nv_pairs_list); + } + + /***************************************************************************/ + /* Iterate through the linked lists, freeing memory. */ + /***************************************************************************/ + field_name = dlist_pop_last(&throttle_spec->suppressed_field_names); + while (field_name != NULL) + { + free(field_name); + field_name = dlist_pop_last(&throttle_spec->suppressed_field_names); + } + + nv_pairs = dlist_pop_last(&throttle_spec->suppressed_nv_pairs_list); + while (nv_pairs != NULL) + { + evel_throttle_free_nv_pair(nv_pairs); + nv_pairs = dlist_pop_last(&throttle_spec->suppressed_nv_pairs_list); + } + + free(throttle_spec); + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Free resources associated with a single ::EVEL_SUPPRESSED_NV_PAIR. + * + * @param nv_pair The ::EVEL_SUPPRESSED_NV_PAIR to free. + *****************************************************************************/ +void evel_throttle_free_nv_pair(EVEL_SUPPRESSED_NV_PAIRS * nv_pairs) +{ + char * suppressed_name; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(nv_pairs != NULL); + + /***************************************************************************/ + /* Free any hash tables. */ + /***************************************************************************/ + if (nv_pairs->hash_nv_pair_names != NULL) + { + hdestroy_r(nv_pairs->hash_nv_pair_names); + free(nv_pairs->hash_nv_pair_names); + } + + /***************************************************************************/ + /* Iterate through the linked lists, freeing memory. */ + /***************************************************************************/ + suppressed_name = dlist_pop_last(&nv_pairs->suppressed_nv_pair_names); + while (suppressed_name != NULL) + { + free(suppressed_name); + suppressed_name = dlist_pop_last(&nv_pairs->suppressed_nv_pair_names); + } + if (nv_pairs->nv_pair_field_name != NULL) + { + free(nv_pairs->nv_pair_field_name); + } + free(nv_pairs); + + 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 the command was handled, false otherwise. + *****************************************************************************/ +bool evel_handle_command_list(const MEMORY_CHUNK * const chunk, + const jsmntok_t * const json_tokens, + const int num_tokens, + MEMORY_CHUNK * const post) +{ + EVEL_JSON_STACK stack; + EVEL_JSON_STACK * json_stack = &stack; + EVEL_JSON_STACK_ENTRY * entry; + + bool json_ok = true; + int token_index = 0; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(chunk != NULL); + assert(json_tokens != NULL); + assert(num_tokens < EVEL_MAX_RESPONSE_TOKENS); + + /***************************************************************************/ + /* Collect one top-level item. */ + /***************************************************************************/ + evel_init_json_stack(json_stack, chunk); + + /***************************************************************************/ + /* Initialize JSON processing variables. */ + /***************************************************************************/ + evel_provide_throttling_state = false; + evel_command_type_value = NULL; + evel_measurement_interval_value = NULL; + evel_throttle_spec_domain_value = NULL; + evel_throttle_spec_domain = EVEL_MAX_DOMAINS; + evel_temp_throttle = NULL; + evel_json_command_state = EVEL_JCS_START; + + /***************************************************************************/ + /* Loop through the tokens, keeping a stack of state representing the */ + /* nested JSON structure (see json_state). We also track our way through */ + /* the ::EVEL_JSON_COMMAND_STATE as we go. */ + /***************************************************************************/ + while (json_ok && (token_index < num_tokens)) + { + const jsmntok_t * const token = &json_tokens[token_index]; + + if (EVEL_DEBUG_ON()) + { + evel_debug_token(chunk, token); + } + + /*************************************************************************/ + /* We may have popped or pushed, so always re-evaluate the stack entry. */ + /*************************************************************************/ + entry = &json_stack->entry[json_stack->level]; + + switch(token->type) + { + case JSMN_OBJECT: + if ((entry->json_state == EVEL_JSON_ITEM) || + (entry->json_state == EVEL_JSON_VALUE)) + { + json_ok = evel_stack_push(json_stack, token->size, EVEL_JSON_KEY); + } + else + { + EVEL_ERROR("Unexpected JSON state %d at token %d (%d)", + entry->json_state, token_index, token->type); + json_ok = false; + } + break; + + case JSMN_ARRAY: + if ((entry->json_state == EVEL_JSON_ITEM) || + (entry->json_state == EVEL_JSON_VALUE)) + { + json_ok = evel_stack_push(json_stack, token->size, EVEL_JSON_ITEM); + } + else + { + EVEL_ERROR("Unexpected JSON state %d at token %d (%d)", + entry->json_state, token_index, token->type); + json_ok = false; + } + break; + + case JSMN_STRING: + if (entry->json_state == EVEL_JSON_KEY) + { + evel_stack_store_key(json_stack, token); + } + else if (entry->json_state == EVEL_JSON_VALUE) + { + evel_stack_store_value(json_stack, token); + } + else if (entry->json_state == EVEL_JSON_ITEM) + { + evel_stack_store_item(json_stack, token); + } + else + { + EVEL_ERROR("Unexpected JSON state %d at token %d (%d)", + entry->json_state, token_index, token->type); + json_ok = false; + } + break; + + case JSMN_PRIMITIVE: + if (entry->json_state == EVEL_JSON_VALUE) + { + evel_stack_store_value(json_stack, token); + } + else if (entry->json_state == EVEL_JSON_ITEM) + { + evel_stack_store_item(json_stack, token); + } + else + { + EVEL_ERROR("Unexpected JSON state %d at token %d (%d)", + entry->json_state, token_index, token->type); + json_ok = false; + } + break; + + case JSMN_UNDEFINED: + default: + EVEL_ERROR("Unexpected JSON format at token %d (%d)", + token_index, token->type); + json_ok = false; + break; + } + + /*************************************************************************/ + /* Pop the stack if we're counted enough nested items. */ + /*************************************************************************/ + evel_stack_pop(json_stack); + + token_index++; + } + + /***************************************************************************/ + /* Cleanup the stack - we may have exited without winding it back, if the */ + /* input was not well formed. */ + /***************************************************************************/ + evel_stack_cleanup(json_stack); + + /***************************************************************************/ + /* We may want to generate and POST a response to the command list. */ + /***************************************************************************/ + if (json_ok) + { + evel_command_list_response(post); + } + + /***************************************************************************/ + /* Make sure we're clean on exit. */ + /***************************************************************************/ + assert(evel_command_type_value == NULL); + assert(evel_measurement_interval_value == NULL); + assert(evel_throttle_spec_domain_value == NULL); + assert(evel_throttle_spec_domain == EVEL_MAX_DOMAINS); + assert(evel_temp_throttle == NULL); + + EVEL_EXIT(); + + return json_ok; +} + +/**************************************************************************//** + * Copy a copy of an element, in string form. + * + * The caller must manage memory allocated for the copied string. + * + * @param chunk Memory chunk containing the JSON buffer. + * @param token The token to copy from. + * @return the copy of the element. + *****************************************************************************/ +char * evel_stack_strdup(const MEMORY_CHUNK * const chunk, + const jsmntok_t * const token) +{ + char temp_char; + char * result; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Call strdup to copy the string, inserting a temporary \0 for the call. */ + /***************************************************************************/ + temp_char = chunk->memory[token->end]; + chunk->memory[token->end] = '\0'; + result = strdup(chunk->memory + token->start); + assert(result != NULL); + chunk->memory[token->end] = temp_char; + + EVEL_EXIT(); + + return result; +} + +/**************************************************************************//** + * Copy a copy of an element, in string form. + * + * @param json_stack The JSON stack to initialize. + * @param chunk The underlying memory chunk used for parsing. + *****************************************************************************/ +void evel_init_json_stack(EVEL_JSON_STACK * json_stack, + const MEMORY_CHUNK * const chunk) +{ + EVEL_JSON_STACK_ENTRY * entry; + + EVEL_ENTER(); + + json_stack->level = 0; + entry = json_stack->entry; + entry->json_state = EVEL_JSON_ITEM; + entry->json_count = 0; + entry->num_required = 1; + entry->json_key = NULL; + json_stack->chunk = chunk; + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Push a new entry on the stack + * + * @param json_stack The stack. + * @param num_required The number of elements required. + * @param new_state The state for the new entry. + * @return false if we cannot push onto the stack. + *****************************************************************************/ +bool evel_stack_push(EVEL_JSON_STACK * const json_stack, + const int num_required, + const EVEL_JSON_STATE new_state) +{ + EVEL_JSON_STACK_ENTRY * entry; + char * key; + bool result; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(json_stack != NULL); + assert(json_stack->level >= 0); + assert(json_stack->level < EVEL_JSON_STACK_DEPTH); + assert((new_state == EVEL_JSON_ITEM) || (new_state == EVEL_JSON_KEY)); + + /***************************************************************************/ + /* Check nesting depth, and stop processing if we hit the limit. */ + /***************************************************************************/ + if ((json_stack->level + 1) >= EVEL_JSON_STACK_DEPTH) + { + EVEL_ERROR("JSON Nesting is too deep - stop processing"); + result = false; + goto exit_label; + } + + /***************************************************************************/ + /* Evaluate cases where we recurse and are interested in the contents. */ + /***************************************************************************/ + entry = &json_stack->entry[json_stack->level]; + key = entry->json_key; + + /***************************************************************************/ + /* Note that this is the key before we drop a level. */ + /***************************************************************************/ + if (key != NULL) + { + EVEL_DEBUG("Push with key: %s", key); + + switch (evel_json_command_state) + { + case EVEL_JCS_START: + if (strcmp(key, "commandList") == 0) + { + evel_set_command_state(EVEL_JCS_COMMAND_LIST); + } + break; + + case EVEL_JCS_COMMAND_LIST_ENTRY: + if (strcmp(key, "command") == 0) + { + evel_open_command(); + evel_set_command_state(EVEL_JCS_COMMAND); + } + break; + + case EVEL_JCS_COMMAND: + if (strcmp(key, "eventDomainThrottleSpecification") == 0) + { + evel_open_throttle_spec(); + evel_set_command_state(EVEL_JCS_SPEC); + } + break; + + case EVEL_JCS_SPEC: + if (strcmp(key, "suppressedFieldNames") == 0) + { + evel_set_command_state(EVEL_JCS_FIELD_NAMES); + } + else if (strcmp(key, "suppressedNvPairsList") == 0) + { + evel_set_command_state(EVEL_JCS_PAIRS_LIST); + } + break; + + case EVEL_JCS_PAIRS_LIST_ENTRY: + if (strcmp(key, "suppressedNvPairNames") == 0) + { + evel_set_command_state(EVEL_JCS_NV_PAIR_NAMES); + } + break; + + case EVEL_JCS_FIELD_NAMES: + case EVEL_JCS_PAIRS_LIST: + case EVEL_JCS_NV_PAIR_NAMES: + default: + EVEL_ERROR("Unexpected JSON key %s in state %d", + key, + evel_json_command_state); + break; + } + } + else + { + EVEL_DEBUG("Push with no key"); + + /*************************************************************************/ + /* If we're pushing without a key, then we're in an array. We switch */ + /* state based on the existing state and stack level. */ + /*************************************************************************/ + const int COMMAND_LIST_LEVEL = 2; + const int NV_PAIRS_LIST_LEVEL = 6; + + if ((evel_json_command_state == EVEL_JCS_PAIRS_LIST) && + (json_stack->level == NV_PAIRS_LIST_LEVEL)) + { + /***********************************************************************/ + /* We are entering an object within the "suppressedNvPairsList" array. */ + /***********************************************************************/ + evel_open_nv_pairs_list_entry(); + evel_set_command_state(EVEL_JCS_PAIRS_LIST_ENTRY); + } + + if ((evel_json_command_state == EVEL_JCS_COMMAND_LIST) && + (json_stack->level == COMMAND_LIST_LEVEL)) + { + /***********************************************************************/ + /* We are entering an object within the "commandList" array. */ + /***********************************************************************/ + evel_set_command_state(EVEL_JCS_COMMAND_LIST_ENTRY); + } + } + + /***************************************************************************/ + /* Push the stack and initialize the entry. */ + /***************************************************************************/ + json_stack->level++; + entry++; + EVEL_DEBUG("Stack Push -> %d", json_stack->level); + entry = &json_stack->entry[json_stack->level]; + entry->json_count = 0; + entry->num_required = num_required; + entry->json_state = new_state; + entry->json_key = NULL; + result = true; + +exit_label: + + EVEL_EXIT(); + + return result; +} + +/**************************************************************************//** + * Pop any stack entries which have collected the required number of items. + * + * @param json_stack The stack. + *****************************************************************************/ +void evel_stack_pop(EVEL_JSON_STACK * const json_stack) +{ + EVEL_JSON_STACK_ENTRY * entry; + char * key; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(json_stack != NULL); + assert(json_stack->level >= 0); + assert(json_stack->level < EVEL_JSON_STACK_DEPTH); + + entry = &json_stack->entry[json_stack->level]; + while ((json_stack->level > 0) && (entry->json_count == entry->num_required)) + { + key = entry->json_key; + + switch (evel_json_command_state) + { + case EVEL_JCS_COMMAND_LIST: + evel_set_command_state(EVEL_JCS_START); + break; + + case EVEL_JCS_COMMAND_LIST_ENTRY: + evel_set_command_state(EVEL_JCS_COMMAND_LIST); + break; + + case EVEL_JCS_COMMAND: + evel_close_command(); + evel_set_command_state(EVEL_JCS_COMMAND_LIST_ENTRY); + break; + + case EVEL_JCS_SPEC: + evel_close_throttle_spec(); + evel_set_command_state(EVEL_JCS_COMMAND); + break; + + case EVEL_JCS_FIELD_NAMES: + evel_set_command_state(EVEL_JCS_SPEC); + break; + + case EVEL_JCS_PAIRS_LIST: + evel_set_command_state(EVEL_JCS_SPEC); + break; + + case EVEL_JCS_PAIRS_LIST_ENTRY: + evel_close_nv_pairs_list_entry(); + evel_set_command_state(EVEL_JCS_PAIRS_LIST); + break; + + case EVEL_JCS_NV_PAIR_NAMES: + evel_set_command_state(EVEL_JCS_PAIRS_LIST_ENTRY); + break; + + default: + break; + } + + /*************************************************************************/ + /* Free off any key that was duplicated and stored. */ + /*************************************************************************/ + if (key != NULL) + { + free(key); + entry->json_key = NULL; + } + + /*************************************************************************/ + /* We just reached the required number of key-value pairs or items, so */ + /* pop the stack. */ + /*************************************************************************/ + json_stack->level--; + entry--; + + EVEL_DEBUG("Stack Pop -> %d", json_stack->level); + + /*************************************************************************/ + /* We just completed collection of an ITEM (within an ARRAY) or a VALUE */ + /* (within an OBJECT). Either way, we need to count it. */ + /*************************************************************************/ + entry->json_count++; + + /*************************************************************************/ + /* If we just completed a VALUE, then we expect the next element to be a */ + /* key, if there is a next element. */ + /*************************************************************************/ + if (entry->json_state == EVEL_JSON_VALUE) + { + entry->json_state = EVEL_JSON_KEY; + } + } + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Pop all stack entries, freeing any memory as we go. + * + * @param json_stack The stack. + *****************************************************************************/ +void evel_stack_cleanup(EVEL_JSON_STACK * const json_stack) +{ + EVEL_JSON_STACK_ENTRY * entry; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(json_stack != NULL); + assert(json_stack->level >= 0); + assert(json_stack->level < EVEL_JSON_STACK_DEPTH); + + entry = &json_stack->entry[json_stack->level]; + while ((json_stack->level > 0)) + { + /*************************************************************************/ + /* Free off any key that was duplicated and stored. */ + /*************************************************************************/ + if (entry->json_key != NULL) + { + free(entry->json_key); + entry->json_key = NULL; + } + + /*************************************************************************/ + /* We just reached the required number of key-value pairs or items, so */ + /* pop the stack. */ + /*************************************************************************/ + json_stack->level--; + entry--; + } + + /***************************************************************************/ + /* If we hit EVEL_JSON_STACK_DEPTH, we exit the loop and can leave these */ + /* values hanging - so clean them up. */ + /***************************************************************************/ + if (evel_command_type_value != NULL) + { + free(evel_command_type_value); + evel_command_type_value = NULL; + } + if (evel_measurement_interval_value != NULL) + { + free(evel_measurement_interval_value); + evel_measurement_interval_value = NULL; + } + if (evel_throttle_spec_domain_value != NULL) + { + free(evel_throttle_spec_domain_value); + evel_throttle_spec_domain_value = NULL; + } + evel_throttle_spec_domain = EVEL_MAX_DOMAINS; + if (evel_temp_throttle != NULL) + { + evel_throttle_free(evel_temp_throttle); + evel_temp_throttle = NULL; + } + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Store a key in the JSON stack. + * + * We always store the most recent key at each level in the stack. + * + * @param json_stack The stack. + * @param token The token holding the key. + *****************************************************************************/ +void evel_stack_store_key(EVEL_JSON_STACK * const json_stack, + const jsmntok_t * const token) +{ + EVEL_JSON_STACK_ENTRY * entry; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(json_stack != NULL); + assert(json_stack->level >= 0); + assert(json_stack->level < EVEL_JSON_STACK_DEPTH); + + /***************************************************************************/ + /* Free any previously stored key, replacing it with the new one. */ + /***************************************************************************/ + entry = &json_stack->entry[json_stack->level]; + if (entry->json_key != NULL) + { + free(entry->json_key); + } + entry->json_key = evel_stack_strdup(json_stack->chunk, token); + + /***************************************************************************/ + /* Switch state to collecting the corresponding value. */ + /***************************************************************************/ + entry->json_state = EVEL_JSON_VALUE; + + EVEL_DEBUG("Stored key: %s", entry->json_key); + EVEL_EXIT(); +} + +/**************************************************************************//** + * Store a value in the JSON stack. + * + * @param json_stack The stack. + * @param token The token holding the value. + *****************************************************************************/ +void evel_stack_store_value(EVEL_JSON_STACK * const json_stack, + const jsmntok_t * const token) +{ + EVEL_JSON_STACK_ENTRY * entry; + char * value; + bool stored; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(json_stack != NULL); + assert(json_stack->level >= 0); + assert(json_stack->level < EVEL_JSON_STACK_DEPTH); + + /***************************************************************************/ + /* Based on the (key, state), work out whether we're expecting a value, */ + /* then store or ignore it as required. */ + /***************************************************************************/ + entry = &json_stack->entry[json_stack->level]; + value = evel_stack_strdup(json_stack->chunk, token); + stored = false; + EVEL_DEBUG("Store value: %s", value); + + switch (evel_json_command_state) + { + case EVEL_JCS_COMMAND: + if (strcmp(entry->json_key, "commandType") == 0) + { + evel_command_type_value = value; + stored = true; + } + else if (strcmp(entry->json_key, "measurementInterval") == 0) + { + evel_measurement_interval_value = value; + stored = true; + } + break; + + case EVEL_JCS_SPEC: + if (strcmp(entry->json_key, "eventDomain") == 0) + { + evel_throttle_spec_domain_value = value; + stored = true; + } + break; + + case EVEL_JCS_PAIRS_LIST_ENTRY: + if (strcmp(entry->json_key, "nvPairFieldName") == 0) + { + evel_store_nv_pair_field_name(value); + stored = true; + } + break; + + default: + EVEL_DEBUG("Ignoring value in state: %s", + evel_jcs_strings[evel_json_command_state]); + break; + } + + if (!stored) + { + EVEL_DEBUG("Ignored value: %s", value); + free(value); + } + + /***************************************************************************/ + /* Switch state to another key. */ + /***************************************************************************/ + entry->json_state = EVEL_JSON_KEY; + + /***************************************************************************/ + /* Count the key-value pair. */ + /***************************************************************************/ + entry->json_count++; + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Store an item in the JSON stack - a string or primitive in an array. + * + * @param json_stack The stack. + * @param token The token holding the item. + *****************************************************************************/ +void evel_stack_store_item(EVEL_JSON_STACK * const json_stack, + const jsmntok_t * const token) +{ + EVEL_JSON_STACK_ENTRY * entry; + char * item; + bool stored; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(json_stack != NULL); + assert(json_stack->level >= 0); + assert(json_stack->level < EVEL_JSON_STACK_DEPTH); + + /***************************************************************************/ + /* Based on the state, work out whether we're expecting an item, then */ + /* store or ignore it as required. */ + /***************************************************************************/ + entry = &json_stack->entry[json_stack->level]; + item = evel_stack_strdup(json_stack->chunk, token); + stored = false; + EVEL_DEBUG("Store item: %s", item); + + switch (evel_json_command_state) + { + case EVEL_JCS_NV_PAIR_NAMES: + evel_store_nv_pair_name(item); + stored = true; + break; + + case EVEL_JCS_FIELD_NAMES: + evel_store_suppressed_field_name(item); + stored = true; + break; + + default: + EVEL_DEBUG("Ignoring item in state: %s", + evel_jcs_strings[evel_json_command_state]); + break; + } + + if (!stored) + { + EVEL_DEBUG("Ignored item: %s", item); + free(item); + } + + /***************************************************************************/ + /* We need another item. This is purely defensive. */ + /***************************************************************************/ + entry->json_state = EVEL_JSON_ITEM; + + /***************************************************************************/ + /* Count the item. */ + /***************************************************************************/ + entry->json_count++; +} + +/**************************************************************************//** + * Set the JSON command state to a new value. + * + * @param new_state The new state to set. + *****************************************************************************/ +void evel_set_command_state(const EVEL_JSON_COMMAND_STATE new_state) +{ + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(evel_json_command_state < EVEL_JCS_MAX); + assert(new_state < EVEL_JCS_MAX); + + /***************************************************************************/ + /* Provide common debug, and set the new state. */ + /***************************************************************************/ + EVEL_DEBUG("Command State: %s -> %s", + evel_jcs_strings[evel_json_command_state], + evel_jcs_strings[new_state]); + evel_json_command_state = new_state; + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Produce debug output from a JSON token. + * + * @param chunk Memory chunk containing the JSON buffer. + * @param token Token to dump. + *****************************************************************************/ +void evel_debug_token(const MEMORY_CHUNK * const chunk, + const jsmntok_t * const token) +{ + char temp_char; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(token->type > 0); + assert(token->type < JSON_TOKEN_TYPES); + + /***************************************************************************/ + /* Log the token, leaving it in the state in which it started. */ + /***************************************************************************/ + temp_char = chunk->memory[token->end]; + chunk->memory[token->end] = '\0'; + EVEL_DEBUG("JSON token type: %s", evel_json_token_strings[token->type]); + EVEL_DEBUG("JSON token: %s", chunk->memory + token->start); + chunk->memory[token->end] = temp_char; + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Post a response to the commandList. + * + * @param post Memory chunk in which to post a response. + *****************************************************************************/ +void evel_command_list_response(MEMORY_CHUNK * const post) +{ + char * json_post; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(post != NULL); + assert(post->memory == NULL); + + if (evel_provide_throttling_state) + { + EVEL_DEBUG("Provide throttling state"); + + /*************************************************************************/ + /* Encode the response, making it printf-able for debug. */ + /*************************************************************************/ + json_post = malloc(EVEL_MAX_JSON_BODY); + assert(json_post != NULL); + post->size = evel_json_encode_throttle(json_post, EVEL_MAX_JSON_BODY - 1); + post->memory = json_post; + post->memory[post->size] = '\0'; + evel_provide_throttling_state = false; + } + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Encode the full throttling specification according to AT&T's schema. + * + * @param json Pointer to where to store the JSON encoded data. + * @param max_size Size of storage available in json_body. + * @returns Number of bytes actually written. + *****************************************************************************/ +int evel_json_encode_throttle(char * const json, const int max_size) +{ + bool throttled; + int domain; + int offset; + bool domain_added; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(json != NULL); + assert(max_size > 0); + + /***************************************************************************/ + /* Work out if we're throttled. */ + /***************************************************************************/ + throttled = false; + for (domain = EVEL_DOMAIN_FAULT; domain < EVEL_MAX_DOMAINS; domain++) + { + if (evel_throttle_spec[domain] != NULL) + { + throttled = true; + } + } + + /***************************************************************************/ + /* Encode the response. */ + /***************************************************************************/ + offset = 0; + offset += snprintf(json + offset, max_size - offset, + "{\"eventThrottlingState\": {"); + offset += snprintf(json + offset, max_size - offset, + "\"eventThrottlingMode\": \"%s\"", + throttled ? "throttled" : "normal"); + if (throttled) + { + offset += snprintf(json + offset, max_size - offset, + ", \"eventDomainThrottleSpecificationList\": ["); + + domain_added = false; + for (domain = EVEL_DOMAIN_FAULT; domain < EVEL_MAX_DOMAINS; domain++) + { + if (evel_throttle_spec[domain] != NULL) + { + if (domain_added) + { + offset += snprintf(json + offset, max_size - offset, ", "); + } + + offset += evel_json_encode_throttle_spec(json + offset, + max_size - offset, + domain); + domain_added = true; + } + } + + offset += snprintf(json + offset, max_size - offset, "]"); + } + + offset += snprintf(json + offset, max_size - offset, "}}"); + + EVEL_EXIT(); + + return offset; +} + +/**************************************************************************//** + * Encode a throttling specification for a domain. + * + * @param json Pointer to where to store the JSON encoded data. + * @param max_size Size of storage available in json_body. + * @returns Number of bytes actually written. + *****************************************************************************/ +int evel_json_encode_throttle_spec(char * const json, + const int max_size, + const EVEL_EVENT_DOMAINS domain) +{ + int offset; + EVEL_THROTTLE_SPEC * throttle_spec; + DLIST_ITEM * dlist_item; + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(domain >= EVEL_DOMAIN_FAULT); + assert(domain < EVEL_MAX_DOMAINS); + assert(evel_throttle_spec[domain] != NULL); + + throttle_spec = evel_throttle_spec[domain]; + + /***************************************************************************/ + /* Encode the domain. */ + /***************************************************************************/ + offset = 0; + offset += snprintf(json + offset, max_size - offset, + "{"); + offset += snprintf(json + offset, max_size - offset, + "\"eventDomain\": \"%s\"", + evel_domain_strings[domain]); + + /***************************************************************************/ + /* Encode "suppressedFieldNames". */ + /***************************************************************************/ + dlist_item = dlist_get_first(&throttle_spec->suppressed_field_names); + if (dlist_item != NULL) + { + offset += snprintf(json + offset, max_size - offset, + ", \"suppressedFieldNames\": ["); + while (dlist_item != NULL) + { + char * suppressed_field = dlist_item->item; + assert(suppressed_field != NULL); + + offset += snprintf(json + offset, max_size - offset, + "\"%s\"", suppressed_field); + dlist_item = dlist_get_next(dlist_item); + if (dlist_item != NULL) + { + offset += snprintf(json + offset, max_size - offset, ", "); + } + } + + offset += snprintf(json + offset, max_size - offset, "]"); + } + + /***************************************************************************/ + /* Encode "suppressedNvPairsList". */ + /***************************************************************************/ + dlist_item = dlist_get_first(&throttle_spec->suppressed_nv_pairs_list); + if (dlist_item != NULL) + { + offset += snprintf(json + offset, max_size - offset, + ", \"suppressedNvPairsList\": ["); + while (dlist_item != NULL) + { + offset += evel_json_encode_nv_pairs(json + offset, + max_size - offset, + dlist_item->item); + dlist_item = dlist_get_next(dlist_item); + if (dlist_item != NULL) + { + offset += snprintf(json + offset, max_size - offset, ", "); + } + } + + offset += snprintf(json + offset, max_size - offset, "]"); + } + + offset += snprintf(json + offset, max_size - offset, "}"); + + EVEL_EXIT(); + + return offset; +} + +/**************************************************************************//** + * Encode a single "suppressedNvPairsListEntry". + * + * @param json Pointer to where to store the JSON encoded data. + * @param max_size Size of storage available in json_body. + * @returns Number of bytes actually written. + *****************************************************************************/ +int evel_json_encode_nv_pairs(char * const json, + const int max_size, + EVEL_SUPPRESSED_NV_PAIRS * nv_pairs) +{ + DLIST_ITEM * dlist_item; + char * name; + int offset; + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(nv_pairs != NULL); + assert(nv_pairs->nv_pair_field_name != NULL); + assert(!dlist_is_empty(&nv_pairs->suppressed_nv_pair_names)); + + /***************************************************************************/ + /* Encode it. */ + /***************************************************************************/ + offset = 0; + offset += snprintf(json + offset, max_size - offset, "{"); + offset += snprintf(json + offset, max_size - offset, + "\"nvPairFieldName\": \"%s\"", + nv_pairs->nv_pair_field_name); + dlist_item = dlist_get_first(&nv_pairs->suppressed_nv_pair_names); + offset += snprintf(json + offset, max_size - offset, + ", \"suppressedNvPairNames\": ["); + while (dlist_item != NULL) + { + name = dlist_item->item; + assert(name != NULL); + offset += snprintf(json + offset, max_size - offset, "\"%s\"", name); + dlist_item = dlist_get_next(dlist_item); + if (dlist_item != NULL) + { + offset += snprintf(json + offset, max_size - offset, ", "); + } + } + offset += snprintf(json + offset, max_size - offset, "]"); + offset += snprintf(json + offset, max_size - offset, "}"); + + EVEL_EXIT(); + + return offset; +} + +/**************************************************************************//** + * Method called when we open a "command" object. + *****************************************************************************/ +void evel_open_command() +{ + EVEL_ENTER(); + + /***************************************************************************/ + /* Make some assertions. */ + /***************************************************************************/ + assert(evel_command_type_value == NULL); + assert(evel_measurement_interval_value == NULL); + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Method called when we close a "command" object. + *****************************************************************************/ +void evel_close_command() +{ + EVEL_ENTER(); + + /***************************************************************************/ + /* If a commandType was provided, fan out and handle it now what we have */ + /* fathered all related information. */ + /* */ + /* Note that we handle throttling specification and measurement interval */ + /* updates immediately on closing the command (not the list). We could */ + /* reject *all* commands in a list if any of them are invalid, but we are */ + /* take a best-effort strategy here - any valid-looking command gets */ + /* implemented regardless of what follows. */ + /***************************************************************************/ + if (evel_command_type_value != NULL) + { + EVEL_DEBUG("Closing command %s", evel_command_type_value); + + if (strcmp(evel_command_type_value, "provideThrottlingState") == 0) + { + evel_provide_throttling_state = true; + } + else if (strcmp(evel_command_type_value, "throttlingSpecification") == 0) + { + evel_set_throttling_spec(); + } + else if (strcmp(evel_command_type_value, "measurementIntervalChange") == 0) + { + evel_set_measurement_interval(); + } + else + { + EVEL_ERROR("Ignoring unknown commandType: %s\n", + evel_command_type_value); + } + + /*************************************************************************/ + /* Free the captured "commandType" value. */ + /*************************************************************************/ + free(evel_command_type_value); + evel_command_type_value = NULL; + } + + /***************************************************************************/ + /* There could be an unused working throttle spec at this point - if the */ + /* "throttlingSpecification" commandType was not provided, or an invalid */ + /* domain was provided, or was not provided at all. */ + /***************************************************************************/ + if (evel_temp_throttle != NULL) + { + evel_throttle_free(evel_temp_throttle); + evel_temp_throttle = NULL; + } + + /***************************************************************************/ + /* Similarly, the domain could be set. */ + /***************************************************************************/ + evel_throttle_spec_domain = EVEL_MAX_DOMAINS; + + /***************************************************************************/ + /* There could be an unused measurement interval value at this point - if */ + /* the "measurementIntervalChange" command was not provided. */ + /***************************************************************************/ + if (evel_measurement_interval_value != NULL) + { + free(evel_measurement_interval_value); + evel_measurement_interval_value = NULL; + } + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Set the provided throttling specification, when the command closes. + *****************************************************************************/ +void evel_set_throttling_spec() +{ + EVEL_ENTER(); + + if ((evel_throttle_spec_domain >= 0) && + (evel_throttle_spec_domain < EVEL_MAX_DOMAINS)) + { + EVEL_DEBUG("Updating throttle spec for domain: %s", + evel_domain_strings[evel_throttle_spec_domain]); + + /*************************************************************************/ + /* Free off the previous throttle specification for the domain, if there */ + /* is one. */ + /*************************************************************************/ + if (evel_throttle_spec[evel_throttle_spec_domain] != NULL) + { + evel_throttle_free(evel_throttle_spec[evel_throttle_spec_domain]); + } + + /*************************************************************************/ + /* Finalize the working throttling spec, if there is one. */ + /*************************************************************************/ + if (evel_temp_throttle != NULL) + { + evel_throttle_finalize(evel_temp_throttle); + } + + /*************************************************************************/ + /* Replace the throttle specification for the domain with the working */ + /* throttle specification. This could be NULL, if an empty throttle */ + /* specification has been received for a domain. */ + /*************************************************************************/ + evel_throttle_spec[evel_throttle_spec_domain] = evel_temp_throttle; + evel_temp_throttle = NULL; + } + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Set the provided measurement interval, when the command closes. + *****************************************************************************/ +void evel_set_measurement_interval() +{ + EVEL_ENTER(); + + if (evel_measurement_interval_value != NULL) + { + const long int value = strtol(evel_measurement_interval_value, NULL, 10); + + if ((value >= 0) && (value <= INT_MAX)) + { + /***********************************************************************/ + /* Lock, update, unlock. */ + /***********************************************************************/ + EVEL_DEBUG("Updating measurement interval to %d\n", value); + + pthread_mutex_lock(&evel_measurement_interval_mutex); + evel_measurement_interval = value; + pthread_mutex_unlock(&evel_measurement_interval_mutex); + } + else + { + EVEL_ERROR("Ignoring invalid measurement interval: %s", + evel_measurement_interval_value); + } + } + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Method called when we open an "eventDomainThrottleSpecification" object. + *****************************************************************************/ +void evel_open_throttle_spec() +{ + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(evel_throttle_spec_domain_value == NULL); + assert(evel_throttle_spec_domain == EVEL_MAX_DOMAINS); + assert(evel_temp_throttle == NULL); + + /***************************************************************************/ + /* Allocate and initialize an ::EVEL_THROTTLE_SPEC in which to hold */ + /* captured JSON elements. */ + /***************************************************************************/ + evel_temp_throttle = malloc(sizeof(EVEL_THROTTLE_SPEC)); + assert(evel_temp_throttle != NULL); + dlist_initialize(&evel_temp_throttle->suppressed_field_names); + dlist_initialize(&evel_temp_throttle->suppressed_nv_pairs_list); + evel_temp_throttle->hash_field_names = NULL; + evel_temp_throttle->hash_nv_pairs_list = NULL; + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Method called when we close an "eventDomainThrottleSpecification" object. + *****************************************************************************/ +void evel_close_throttle_spec() +{ + EVEL_ENTER(); + + /***************************************************************************/ + /* Decode, free and blank a captured event domain value. */ + /***************************************************************************/ + if (evel_throttle_spec_domain_value != NULL) + { + evel_throttle_spec_domain = + evel_decode_domain(evel_throttle_spec_domain_value); + free(evel_throttle_spec_domain_value); + evel_throttle_spec_domain_value = NULL; + } + + /***************************************************************************/ + /* Free off an empty working throttle spec, to stop it being used. This */ + /* state should be represented by a NULL pointer for the domain. */ + /***************************************************************************/ + if (evel_temp_throttle != NULL) + { + if (dlist_is_empty(&evel_temp_throttle->suppressed_field_names) && + dlist_is_empty(&evel_temp_throttle->suppressed_nv_pairs_list)) + { + free(evel_temp_throttle); + evel_temp_throttle = NULL; + } + } + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Convert a value for an "eventDomain" into an ::EVEL_EVENT_DOMAINS. + * + * @param domain_value The domain string value to decode. + * @returns The matching ::EVEL_EVENT_DOMAINS, or ::EVEL_MAX_DOMAINS on error. + *****************************************************************************/ +EVEL_EVENT_DOMAINS evel_decode_domain(char * domain_value) +{ + EVEL_EVENT_DOMAINS result; + int ii; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(domain_value != NULL); + + result = EVEL_MAX_DOMAINS; + for (ii = EVEL_DOMAIN_FAULT; ii < EVEL_MAX_DOMAINS; ii++) + { + assert(evel_domain_strings[ii] != NULL); + if (strcmp(evel_domain_strings[ii], domain_value) == 0) + { + result = ii; + } + } + + EVEL_EXIT(); + + return result; +} + +/**************************************************************************//** + * Method called when we open a "suppressedNvPairsListEntry" object. + *****************************************************************************/ +void evel_open_nv_pairs_list_entry() +{ + EVEL_SUPPRESSED_NV_PAIRS * nv_pairs; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(evel_temp_throttle != NULL); + + /***************************************************************************/ + /* Allocate and initialize an ::EVEL_SUPPRESSED_NV_PAIRS, and add it to */ + /* the list. */ + /***************************************************************************/ + nv_pairs = malloc(sizeof(EVEL_SUPPRESSED_NV_PAIRS)); + assert(nv_pairs != NULL); + nv_pairs->nv_pair_field_name = NULL; + dlist_initialize(&nv_pairs->suppressed_nv_pair_names); + nv_pairs->hash_nv_pair_names = NULL; + dlist_push_last(&evel_temp_throttle->suppressed_nv_pairs_list, nv_pairs); + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Method called when we close a "suppressedNvPairsListEntry" object. + *****************************************************************************/ +void evel_close_nv_pairs_list_entry() +{ + EVEL_SUPPRESSED_NV_PAIRS * nv_pairs; + EVEL_SUPPRESSED_NV_PAIRS * popped; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Get the latest nv pairs. This also performs the required checks. */ + /***************************************************************************/ + nv_pairs = evel_get_last_nv_pairs(); + + /***************************************************************************/ + /* For a "suppressedNvPairsListEntry" to have any meaning, we need both */ + /* "nvPairFieldName" and "suppressedNvPairNames". If we don't, then pop */ + /* and free whatever we just collected. */ + /***************************************************************************/ + if ((nv_pairs->nv_pair_field_name == NULL) || + dlist_is_empty(&nv_pairs->suppressed_nv_pair_names)) + { + popped = dlist_pop_last(&evel_temp_throttle->suppressed_nv_pairs_list); + assert(popped == nv_pairs); + evel_throttle_free_nv_pair(popped); + } + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Store an "nvPairFieldName" value in the working throttle spec. + * + * @param value The value to store. + *****************************************************************************/ +void evel_store_nv_pair_field_name(char * const value) +{ + EVEL_SUPPRESSED_NV_PAIRS * nv_pairs; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Get the latest nv pairs. This also performs the required checks. */ + /***************************************************************************/ + nv_pairs = evel_get_last_nv_pairs(); + + /***************************************************************************/ + /* Store the value. */ + /***************************************************************************/ + nv_pairs->nv_pair_field_name = value; + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Store a "suppressedNvPairNames" item in the working throttle spec. + * + * @param item The item to store. + *****************************************************************************/ +void evel_store_nv_pair_name(char * const item) +{ + EVEL_SUPPRESSED_NV_PAIRS * nv_pairs; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Get the latest nv pairs. This also performs the required checks. */ + /***************************************************************************/ + nv_pairs = evel_get_last_nv_pairs(); + + /***************************************************************************/ + /* Store the item. */ + /***************************************************************************/ + dlist_push_last(&nv_pairs->suppressed_nv_pair_names, item); + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Store a "suppressedFieldNames" item in the working throttle spec. + * + * @param item The item to store. + *****************************************************************************/ +void evel_store_suppressed_field_name(char * const item) +{ + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(evel_temp_throttle != NULL); + + /***************************************************************************/ + /* Store the item. */ + /***************************************************************************/ + dlist_push_last(&evel_temp_throttle->suppressed_field_names, item); + + EVEL_EXIT(); +} + +/**************************************************************************//** + * Get the last added suppressed nv pairs list entry in the working spec. + * + * @returns The last entry. + *****************************************************************************/ +EVEL_SUPPRESSED_NV_PAIRS * evel_get_last_nv_pairs() +{ + DLIST_ITEM * dlist_item; + EVEL_SUPPRESSED_NV_PAIRS * nv_pairs; + + EVEL_ENTER(); + + /***************************************************************************/ + /* Check preconditions. */ + /***************************************************************************/ + assert(evel_temp_throttle != NULL); + + /***************************************************************************/ + /* Get the pair that was added when we opened the list entry. */ + /***************************************************************************/ + dlist_item = dlist_get_last(&evel_temp_throttle->suppressed_nv_pairs_list); + assert(dlist_item != NULL); + nv_pairs = dlist_item->item; + assert(nv_pairs != NULL); + + EVEL_EXIT(); + + return nv_pairs; +} |