Initial VES for DANOS vRouter
[demo.git] / vnfs / VESreporting_vFW5.0_DANOS / evel / evel-library / code / evel_library / evel_throttle.c
1 /*************************************************************************//**
2  *
3  * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
4  *
5  * Unless otherwise specified, all software contained herein is
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and 
15  * limitations under the License.
16  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
17  ****************************************************************************/
18
19 /**************************************************************************//**
20  * @file
21  * Event Manager
22  *
23  * Simple event manager that is responsible for taking events (Heartbeats,
24  * Faults and Measurements) from the ring-buffer and posting them to the API.
25  *
26  ****************************************************************************/
27
28 #define _GNU_SOURCE
29 #include <string.h>
30 #include <assert.h>
31 #include <stdlib.h>
32 #include <limits.h>
33 #include <pthread.h>
34 #include <search.h>
35
36 #include "evel_throttle.h"
37
38 /*****************************************************************************/
39 /* The Event Throttling State for all domains, indexed by                    */
40 /* ::EVEL_EVENT_DOMAINS, corresponding to JSON eventDomain.                  */
41 /*                                                                           */
42 /* A given domain is in a throttled state if ::evel_throttle_spec is         */
43 /* non-NULL.                                                                 */
44 /*****************************************************************************/
45 static EVEL_THROTTLE_SPEC * evel_throttle_spec[EVEL_MAX_DOMAINS];
46
47 /*****************************************************************************/
48 /* The current measurement interval.  Default: MEASUREMENT_INTERVAL_UKNOWN.  */
49 /* Must be protected by evel_measurement_interval_mutex.                     */
50 /*****************************************************************************/
51 static int evel_measurement_interval;
52
53 /*****************************************************************************/
54 /* Mutex protecting evel_measurement_interval from contention between an     */
55 /* EVEL client reading it, and the EVEL event handler updating it.           */
56 /*****************************************************************************/
57 static pthread_mutex_t evel_measurement_interval_mutex;
58
59 /*****************************************************************************/
60 /* Flag stating that we have received a "provideThrottlingState" command.    */
61 /* Set during JSON processing and cleared on sending the throttling state.   */
62 /*****************************************************************************/
63 static bool evel_provide_throttling_state;
64
65 /*****************************************************************************/
66 /* Holder for the "commandType" value during JSON processing.                */
67 /*****************************************************************************/
68 static char * evel_command_type_value;
69
70 /*****************************************************************************/
71 /* Holder for the "measurementInterval" value during JSON processing.        */
72 /*****************************************************************************/
73 static char * evel_measurement_interval_value;
74
75 /*****************************************************************************/
76 /* Holder for the "eventDomain" value during JSON processing.                */
77 /*****************************************************************************/
78 static char * evel_throttle_spec_domain_value;
79
80 /*****************************************************************************/
81 /* Decoded version of ::evel_throttle_spec_domain_value.                     */
82 /*****************************************************************************/
83 static EVEL_EVENT_DOMAINS evel_throttle_spec_domain;
84
85 /*****************************************************************************/
86 /* During JSON processing of a single throttling specification, we collect   */
87 /* parameters in this working ::EVEL_THROTTLE_SPEC                           */
88 /*****************************************************************************/
89 static EVEL_THROTTLE_SPEC * evel_temp_throttle;
90
91 /*****************************************************************************/
92 /* State tracking our progress through the command list                      */
93 /*****************************************************************************/
94 EVEL_JSON_COMMAND_STATE evel_json_command_state;
95
96 /*****************************************************************************/
97 /* Debug strings for ::EVEL_JSON_COMMAND_STATE.                              */
98 /*****************************************************************************/
99 static const char * const evel_jcs_strings[EVEL_JCS_MAX] = {
100   "EVEL_JCS_START",
101   "EVEL_JCS_COMMAND_LIST",
102   "EVEL_JCS_COMMAND_LIST_ENTRY",
103   "EVEL_JCS_COMMAND",
104   "EVEL_JCS_SPEC",
105   "EVEL_JCS_FIELD_NAMES",
106   "EVEL_JCS_PAIRS_LIST",
107   "EVEL_JCS_PAIRS_LIST_ENTRY",
108   "EVEL_JCS_NV_PAIR_NAMES"
109 };
110
111 /*****************************************************************************/
112 /* Debug strings for JSON token type.                                        */
113 /*****************************************************************************/
114 #define JSON_TOKEN_TYPES                (JSMN_PRIMITIVE + 1)
115 static const char * const evel_json_token_strings[JSON_TOKEN_TYPES] = {
116   "JSMN_UNDEFINED",
117   "JSMN_OBJECT",
118   "JSMN_ARRAY",
119   "JSMN_STRING",
120   "JSMN_PRIMITIVE"
121 };
122
123 /*****************************************************************************/
124 /* Debug strings for JSON domains.                                           */
125 /*****************************************************************************/
126 static const char * evel_domain_strings[EVEL_MAX_DOMAINS] = {
127   "internal",
128   "batch",
129   "heartbeat",
130   "fault",
131   "measurementsForVfScaling",
132   "mobileFlow",
133   "report",
134   "serviceEvents",
135   "signaling",
136   "stateChange",
137   "syslog",
138   "other",
139   "voiceQuality",
140   "maxDomain"
141 };
142
143 /*****************************************************************************/
144 /* Local prototypes.                                                         */
145 /*****************************************************************************/
146 static void evel_throttle_finalize(EVEL_THROTTLE_SPEC * throttle_spec);
147 static struct hsearch_data * evel_throttle_hash_create(DLIST * hash_keys);
148 static void evel_throttle_free(EVEL_THROTTLE_SPEC * throttle_spec);
149 static void evel_throttle_free_nv_pair(EVEL_SUPPRESSED_NV_PAIRS * nv_pairs);
150 static void evel_init_json_stack(EVEL_JSON_STACK * json_stack,
151                                  const MEMORY_CHUNK * const chunk);
152 static bool evel_stack_push(EVEL_JSON_STACK * const json_stack,
153                             const int num_required,
154                             const EVEL_JSON_STATE new_state);
155 static void evel_stack_pop(EVEL_JSON_STACK * const json_stack);
156 static void evel_stack_cleanup(EVEL_JSON_STACK * const json_stack);
157 static char * evel_stack_strdup(const MEMORY_CHUNK * const chunk,
158                                 const jsmntok_t * const token);
159 static void evel_stack_store_key(EVEL_JSON_STACK * const json_stack,
160                                  const jsmntok_t * const token);
161 static void evel_stack_store_value(EVEL_JSON_STACK * const json_stack,
162                                    const jsmntok_t * const token);
163 static void evel_stack_store_item(EVEL_JSON_STACK * const json_stack,
164                                   const jsmntok_t * const token);
165 static void evel_set_command_state(const EVEL_JSON_COMMAND_STATE new_state);
166 static void evel_debug_token(const MEMORY_CHUNK * const chunk,
167                              const jsmntok_t * const token);
168 static void evel_command_list_response(MEMORY_CHUNK * const post);
169 static int evel_json_encode_throttle(char * const json, const int max_size);
170 static int evel_json_encode_throttle_spec(char * const json,
171                                           const int max_size,
172                                           const EVEL_EVENT_DOMAINS domain);
173 static int evel_json_encode_nv_pairs(char * const json,
174                                      const int max_size,
175                                      EVEL_SUPPRESSED_NV_PAIRS * nv_pairs);
176 static void evel_close_command();
177 static void evel_open_command();
178 static void evel_set_throttling_spec();
179 static void evel_set_measurement_interval();
180 static void evel_open_throttle_spec();
181 static void evel_close_throttle_spec();
182 static EVEL_EVENT_DOMAINS evel_decode_domain(char * domain_value);
183 static void evel_open_nv_pairs_list_entry();
184 static void evel_close_nv_pairs_list_entry();
185 static void evel_store_nv_pair_field_name(char * const value);
186 static void evel_store_nv_pair_name(char * const item);
187 static void evel_store_suppressed_field_name(char * const item);
188 static EVEL_SUPPRESSED_NV_PAIRS * evel_get_last_nv_pairs();
189
190 /**************************************************************************//**
191  * Return the current measurement interval provided by the Event Listener.
192  *
193  * @returns The current measurement interval
194  * @retval  EVEL_MEASUREMENT_INTERVAL_UKNOWN (0) - interval has not been
195  *          specified
196  *****************************************************************************/
197 int evel_get_measurement_interval()
198 {
199   int result;
200
201   EVEL_ENTER();
202
203   /***************************************************************************/
204   /* Lock, read, unlock.                                                     */
205   /***************************************************************************/
206   pthread_mutex_lock(&evel_measurement_interval_mutex);
207   result = evel_measurement_interval;
208   pthread_mutex_unlock(&evel_measurement_interval_mutex);
209
210   EVEL_EXIT();
211
212   return result;
213 }
214
215 /**************************************************************************//**
216  * Return the ::EVEL_THROTTLE_SPEC for a given domain.
217  *
218  * @param domain        The domain for which to return state.
219  *****************************************************************************/
220 EVEL_THROTTLE_SPEC * evel_get_throttle_spec(EVEL_EVENT_DOMAINS domain)
221 {
222   EVEL_THROTTLE_SPEC * result;
223
224   EVEL_ENTER();
225
226   /***************************************************************************/
227   /* Check preconditions.                                                    */
228   /***************************************************************************/
229   assert(domain < EVEL_MAX_DOMAINS);
230
231   result = evel_throttle_spec[domain];
232
233   EVEL_EXIT();
234
235   return result;
236 }
237
238 /**************************************************************************//**
239  * Determine whether a field_name should be suppressed.
240  *
241  * @param throttle_spec Throttle specification for the domain being encoded.
242  * @param field_name    The field name to encoded or suppress.
243  * @return true if the field_name should be suppressed, false otherwise.
244  *****************************************************************************/
245 bool evel_throttle_suppress_field(EVEL_THROTTLE_SPEC * throttle_spec,
246                                   const char * const field_name)
247 {
248   bool suppress = false;
249
250   EVEL_ENTER();
251
252   /***************************************************************************/
253   /* Check preconditions.                                                    */
254   /***************************************************************************/
255   assert(field_name != NULL);
256
257   /***************************************************************************/
258   /* If the throttle spec and hash table exist, query the field_names table. */
259   /***************************************************************************/
260   if ((throttle_spec != NULL) && (throttle_spec->hash_field_names != NULL))
261   {
262     ENTRY hash_query;
263     ENTRY * hash_result;
264     hash_query.key = (char * const) field_name;
265     suppress = (hsearch_r(hash_query,
266                           FIND,
267                           &hash_result,
268                           throttle_spec->hash_field_names) != 0);
269   }
270
271   EVEL_EXIT();
272
273   return suppress;
274 }
275
276 /**************************************************************************//**
277  * Determine whether a name-value pair should be allowed (not suppressed).
278  *
279  * @param throttle_spec Throttle specification for the domain being encoded.
280  * @param field_name    The field name holding the name-value pairs.
281  * @param name          The name of the name-value pair to encoded or suppress.
282  * @return true if the name-value pair should be suppressed, false otherwise.
283  *****************************************************************************/
284 bool evel_throttle_suppress_nv_pair(EVEL_THROTTLE_SPEC * throttle_spec,
285                                     const char * const field_name,
286                                     const char * const name)
287 {
288   EVEL_SUPPRESSED_NV_PAIRS * nv_pairs;
289   bool hit = false;
290   bool suppress = false;
291
292   EVEL_ENTER();
293
294   /***************************************************************************/
295   /* Check preconditions.                                                    */
296   /***************************************************************************/
297   assert(field_name != NULL);
298   assert(name != NULL);
299
300   /***************************************************************************/
301   /* If the throttle spec and hash table exist, query the nv_pairs table.    */
302   /***************************************************************************/
303   if ((throttle_spec != NULL) && (throttle_spec->hash_nv_pairs_list != NULL))
304   {
305     ENTRY hash_query;
306     ENTRY * hash_result;
307     hash_query.key = (char * const) field_name;
308     hit = (hsearch_r(hash_query,
309                      FIND,
310                      &hash_result,
311                      throttle_spec->hash_nv_pairs_list) != 0);
312     if (hit)
313     {
314       nv_pairs = hash_result->data;
315     }
316   }
317
318   /***************************************************************************/
319   /* If we got a hit, and the nv_pairs and hash table exist, query the       */
320   /* nv_pairs table.                                                         */
321   /***************************************************************************/
322   if (hit && (nv_pairs != NULL) && (nv_pairs->hash_nv_pair_names != NULL))
323   {
324     ENTRY hash_query;
325     ENTRY * hash_result;
326     hash_query.key = (char * const) name;
327     suppress = (hsearch_r(hash_query,
328                           FIND,
329                           &hash_result,
330                           nv_pairs->hash_nv_pair_names) != 0);
331   }
332
333   EVEL_EXIT();
334
335   return suppress;
336 }
337
338 /**************************************************************************//**
339  * Initialize event throttling to the default state.
340  *
341  * Called from ::evel_initialize.
342  *****************************************************************************/
343 void evel_throttle_initialize()
344 {
345   int pthread_rc;
346   int ii;
347
348   EVEL_ENTER();
349
350   for (ii = 0; ii < EVEL_MAX_DOMAINS; ii++)
351   {
352     evel_throttle_spec[ii] = NULL;
353   }
354
355   pthread_rc = pthread_mutex_init(&evel_measurement_interval_mutex, NULL);
356   assert(pthread_rc == 0);
357
358   evel_measurement_interval = EVEL_MEASUREMENT_INTERVAL_UKNOWN;
359
360   EVEL_EXIT();
361 }
362
363 /**************************************************************************//**
364  * Clean up event throttling.
365  *
366  * Called from ::evel_terminate.
367  *****************************************************************************/
368 void evel_throttle_terminate()
369 {
370   int pthread_rc;
371   int ii;
372
373   EVEL_ENTER();
374
375   for (ii = 0; ii < EVEL_MAX_DOMAINS; ii++)
376   {
377     if (evel_throttle_spec[ii] != NULL)
378     {
379       evel_throttle_free(evel_throttle_spec[ii]);
380       evel_throttle_spec[ii] = NULL;
381     }
382   }
383
384   pthread_rc = pthread_mutex_destroy(&evel_measurement_interval_mutex);
385   assert(pthread_rc == 0);
386
387   EVEL_EXIT();
388 }
389
390 /**************************************************************************//**
391  * Finalize a single ::EVEL_THROTTLE_SPEC.
392  *
393  * Now that the specification is collected, build hash tables to simplify the
394  * throttling itself.
395  *
396  * @param throttle_spec The ::EVEL_THROTTLE_SPEC to finalize.
397  *****************************************************************************/
398 void evel_throttle_finalize(EVEL_THROTTLE_SPEC * throttle_spec)
399 {
400   int nv_pairs_count;
401   DLIST_ITEM * dlist_item;
402   ENTRY * add_result;
403
404   EVEL_ENTER();
405
406   /***************************************************************************/
407   /* Check preconditions.                                                    */
408   /***************************************************************************/
409   assert(throttle_spec != NULL);
410
411   /***************************************************************************/
412   /* Populate the hash table for suppressed field names.                     */
413   /***************************************************************************/
414   throttle_spec->hash_field_names =
415              evel_throttle_hash_create(&throttle_spec->suppressed_field_names);
416
417   /***************************************************************************/
418   /* Create the hash table for suppressed nv pairs.                          */
419   /***************************************************************************/
420   nv_pairs_count = dlist_count(&throttle_spec->suppressed_nv_pairs_list);
421   if (nv_pairs_count > 0)
422   {
423     throttle_spec->hash_nv_pairs_list = calloc(1, sizeof(struct hsearch_data));
424     assert(throttle_spec->hash_nv_pairs_list != NULL);
425
426     /*************************************************************************/
427     /* Provide plenty of space in the table - see hcreate_r notes.           */
428     /*************************************************************************/
429     if (hcreate_r(nv_pairs_count * 2, throttle_spec->hash_nv_pairs_list) == 0)
430     {
431       EVEL_ERROR("Failed to create hash table");
432       free(throttle_spec->hash_nv_pairs_list);
433       throttle_spec->hash_nv_pairs_list = NULL;
434     }
435   }
436
437   /***************************************************************************/
438   /* Populate the hash tables under suppressed field names.                  */
439   /***************************************************************************/
440   dlist_item = dlist_get_first(&throttle_spec->suppressed_nv_pairs_list);
441   while (dlist_item != NULL)
442   {
443     EVEL_SUPPRESSED_NV_PAIRS * nv_pairs = dlist_item->item;
444     ENTRY hash_add;
445
446     /*************************************************************************/
447     /* Set the key to the string, and the item to the nv_pairs.              */
448     /*************************************************************************/
449     assert(nv_pairs != NULL);
450     hash_add.key = nv_pairs->nv_pair_field_name;
451     hash_add.data = nv_pairs;
452     hsearch_r(hash_add, ENTER, &add_result, throttle_spec->hash_nv_pairs_list);
453
454     /*************************************************************************/
455     /* Create the nv_pair_names hash since we're in here.                    */
456     /*************************************************************************/
457     nv_pairs->hash_nv_pair_names =
458       evel_throttle_hash_create(&nv_pairs->suppressed_nv_pair_names);
459
460     dlist_item = dlist_get_next(dlist_item);
461   }
462
463   EVEL_EXIT();
464 }
465
466 /**************************************************************************//**
467  * Create and populate a hash table from a DLIST of keys.
468  *
469  * @param hash_keys     Pointer to a DLIST of hash table keys.
470  * @return Pointer to the created hash-table, or NULL on failure.
471  *****************************************************************************/
472 struct hsearch_data * evel_throttle_hash_create(DLIST * hash_keys)
473 {
474   int key_count;
475   struct hsearch_data * hash_table;
476   ENTRY * add_result;
477
478   EVEL_ENTER();
479
480   /***************************************************************************/
481   /* Check preconditions.                                                    */
482   /***************************************************************************/
483   assert(hash_keys != NULL);
484
485   /***************************************************************************/
486   /* Count the keys and if there are any, populate the hash table with them. */
487   /***************************************************************************/
488   key_count = dlist_count(hash_keys);
489   if (key_count > 0)
490   {
491     EVEL_DEBUG("Populating table for %d keys", key_count);
492
493     hash_table = calloc(1, sizeof(struct hsearch_data));
494     assert(hash_table != NULL);
495
496     /*************************************************************************/
497     /* We need to leave plenty of space in the table - see hcreate_r notes.  */
498     /*************************************************************************/
499     if (hcreate_r(key_count * 2, hash_table) != 0)
500     {
501       DLIST_ITEM * dlist_item;
502       dlist_item = dlist_get_first(hash_keys);
503       while (dlist_item != NULL)
504       {
505         assert(dlist_item->item != NULL);
506
507         /*********************************************************************/
508         /* Set the key and data to the item, which is a string in this case. */
509         /*********************************************************************/
510         ENTRY hash_add;
511         hash_add.key = dlist_item->item;
512         hash_add.data = dlist_item->item;
513         hsearch_r(hash_add, ENTER, &add_result, hash_table);
514         dlist_item = dlist_get_next(dlist_item);
515       }
516     }
517     else
518     {
519       EVEL_ERROR("Failed to create hash table");
520       free(hash_table);
521       hash_table = NULL;
522     }
523   }
524   else
525   {
526     hash_table = NULL;
527   }
528
529   EVEL_EXIT();
530
531   return hash_table;
532 }
533
534 /**************************************************************************//**
535  * Free resources associated with a single ::EVEL_THROTTLE_SPEC.
536  *
537  * @param throttle_spec The ::EVEL_THROTTLE_SPEC to free.
538  *****************************************************************************/
539 void evel_throttle_free(EVEL_THROTTLE_SPEC * throttle_spec)
540 {
541   char * field_name;
542   EVEL_SUPPRESSED_NV_PAIRS * nv_pairs;
543
544   EVEL_ENTER();
545
546   /***************************************************************************/
547   /* Check preconditions.                                                    */
548   /***************************************************************************/
549   assert(throttle_spec != NULL);
550
551   /***************************************************************************/
552   /* Free any hash tables.                                                   */
553   /***************************************************************************/
554   if (throttle_spec->hash_field_names != NULL)
555   {
556     hdestroy_r(throttle_spec->hash_field_names);
557     free(throttle_spec->hash_field_names);
558   }
559   if (throttle_spec->hash_nv_pairs_list != NULL)
560   {
561     hdestroy_r(throttle_spec->hash_nv_pairs_list);
562     free(throttle_spec->hash_nv_pairs_list);
563   }
564
565   /***************************************************************************/
566   /* Iterate through the linked lists, freeing memory.                       */
567   /***************************************************************************/
568   field_name = dlist_pop_last(&throttle_spec->suppressed_field_names);
569   while (field_name != NULL)
570   {
571     free(field_name);
572     field_name = dlist_pop_last(&throttle_spec->suppressed_field_names);
573   }
574
575   nv_pairs = dlist_pop_last(&throttle_spec->suppressed_nv_pairs_list);
576   while (nv_pairs != NULL)
577   {
578     evel_throttle_free_nv_pair(nv_pairs);
579     nv_pairs = dlist_pop_last(&throttle_spec->suppressed_nv_pairs_list);
580   }
581
582   free(throttle_spec);
583
584   EVEL_EXIT();
585 }
586
587 /**************************************************************************//**
588  * Free resources associated with a single ::EVEL_SUPPRESSED_NV_PAIR.
589  *
590  * @param nv_pair       The ::EVEL_SUPPRESSED_NV_PAIR to free.
591  *****************************************************************************/
592 void evel_throttle_free_nv_pair(EVEL_SUPPRESSED_NV_PAIRS * nv_pairs)
593 {
594   char * suppressed_name;
595
596   EVEL_ENTER();
597
598   /***************************************************************************/
599   /* Check preconditions.                                                    */
600   /***************************************************************************/
601   assert(nv_pairs != NULL);
602
603   /***************************************************************************/
604   /* Free any hash tables.                                                   */
605   /***************************************************************************/
606   if (nv_pairs->hash_nv_pair_names != NULL)
607   {
608     hdestroy_r(nv_pairs->hash_nv_pair_names);
609     free(nv_pairs->hash_nv_pair_names);
610   }
611
612   /***************************************************************************/
613   /* Iterate through the linked lists, freeing memory.                       */
614   /***************************************************************************/
615   suppressed_name = dlist_pop_last(&nv_pairs->suppressed_nv_pair_names);
616   while (suppressed_name != NULL)
617   {
618     free(suppressed_name);
619     suppressed_name = dlist_pop_last(&nv_pairs->suppressed_nv_pair_names);
620   }
621   if (nv_pairs->nv_pair_field_name != NULL)
622   {
623     free(nv_pairs->nv_pair_field_name);
624   }
625   free(nv_pairs);
626
627   EVEL_EXIT();
628 }
629
630 /**************************************************************************//**
631  * Handle a JSON response from the listener, as a list of tokens from JSMN.
632  *
633  * @param chunk         Memory chunk containing the JSON buffer.
634  * @param json_tokens   Array of tokens to handle.
635  * @param num_tokens    The number of tokens to handle.
636  * @param post          The memory chunk in which to place any resulting POST.
637  * @return true if the command was handled, false otherwise.
638  *****************************************************************************/
639 bool evel_handle_command_list(const MEMORY_CHUNK * const chunk,
640                               const jsmntok_t * const json_tokens,
641                               const int num_tokens,
642                               MEMORY_CHUNK * const post)
643 {
644   EVEL_JSON_STACK stack;
645   EVEL_JSON_STACK * json_stack = &stack;
646   EVEL_JSON_STACK_ENTRY * entry;
647
648   bool json_ok = true;
649   int token_index = 0;
650
651   EVEL_ENTER();
652
653   /***************************************************************************/
654   /* Check preconditions.                                                    */
655   /***************************************************************************/
656   assert(chunk != NULL);
657   assert(json_tokens != NULL);
658   assert(num_tokens < EVEL_MAX_RESPONSE_TOKENS);
659
660   /***************************************************************************/
661   /* Collect one top-level item.                                             */
662   /***************************************************************************/
663   evel_init_json_stack(json_stack, chunk);
664
665   /***************************************************************************/
666   /* Initialize JSON processing variables.                                   */
667   /***************************************************************************/
668   evel_provide_throttling_state = false;
669   evel_command_type_value = NULL;
670   evel_measurement_interval_value = NULL;
671   evel_throttle_spec_domain_value = NULL;
672   evel_throttle_spec_domain = EVEL_MAX_DOMAINS;
673   evel_temp_throttle = NULL;
674   evel_json_command_state = EVEL_JCS_START;
675
676   /***************************************************************************/
677   /* Loop through the tokens, keeping a stack of state representing the      */
678   /* nested JSON structure (see json_state). We also track our way through   */
679   /* the ::EVEL_JSON_COMMAND_STATE as we go.                                 */
680   /***************************************************************************/
681   while (json_ok && (token_index < num_tokens))
682   {
683     const jsmntok_t * const token = &json_tokens[token_index];
684
685     if (EVEL_DEBUG_ON())
686     {
687       evel_debug_token(chunk, token);
688     }
689
690     /*************************************************************************/
691     /* We may have popped or pushed, so always re-evaluate the stack entry.  */
692     /*************************************************************************/
693     entry = &json_stack->entry[json_stack->level];
694
695     switch(token->type)
696     {
697       case JSMN_OBJECT:
698         if ((entry->json_state == EVEL_JSON_ITEM) ||
699             (entry->json_state == EVEL_JSON_VALUE))
700         {
701           json_ok = evel_stack_push(json_stack, token->size, EVEL_JSON_KEY);
702         }
703         else
704         {
705           EVEL_ERROR("Unexpected JSON state %d at token %d (%d)",
706                      entry->json_state, token_index, token->type);
707           json_ok = false;
708         }
709         break;
710
711       case JSMN_ARRAY:
712         if ((entry->json_state == EVEL_JSON_ITEM) ||
713             (entry->json_state == EVEL_JSON_VALUE))
714         {
715           json_ok = evel_stack_push(json_stack, token->size, EVEL_JSON_ITEM);
716         }
717         else
718         {
719           EVEL_ERROR("Unexpected JSON state %d at token %d (%d)",
720                      entry->json_state, token_index, token->type);
721           json_ok = false;
722         }
723         break;
724
725       case JSMN_STRING:
726         if (entry->json_state == EVEL_JSON_KEY)
727         {
728           evel_stack_store_key(json_stack, token);
729         }
730         else if (entry->json_state == EVEL_JSON_VALUE)
731         {
732           evel_stack_store_value(json_stack, token);
733         }
734         else if (entry->json_state == EVEL_JSON_ITEM)
735         {
736           evel_stack_store_item(json_stack, token);
737         }
738         else
739         {
740           EVEL_ERROR("Unexpected JSON state %d at token %d (%d)",
741                      entry->json_state, token_index, token->type);
742           json_ok = false;
743         }
744         break;
745
746       case JSMN_PRIMITIVE:
747         if (entry->json_state == EVEL_JSON_VALUE)
748         {
749           evel_stack_store_value(json_stack, token);
750         }
751         else if (entry->json_state == EVEL_JSON_ITEM)
752         {
753           evel_stack_store_item(json_stack, token);
754         }
755         else
756         {
757           EVEL_ERROR("Unexpected JSON state %d at token %d (%d)",
758                      entry->json_state, token_index, token->type);
759           json_ok = false;
760         }
761         break;
762
763       case JSMN_UNDEFINED:
764       default:
765         EVEL_ERROR("Unexpected JSON format at token %d (%d)",
766                    token_index, token->type);
767         json_ok = false;
768         break;
769     }
770
771     /*************************************************************************/
772     /* Pop the stack if we're counted enough nested items.                   */
773     /*************************************************************************/
774     evel_stack_pop(json_stack);
775
776     token_index++;
777   }
778
779   /***************************************************************************/
780   /* Cleanup the stack - we may have exited without winding it back, if the  */
781   /* input was not well formed.                                              */
782   /***************************************************************************/
783   evel_stack_cleanup(json_stack);
784
785   /***************************************************************************/
786   /* We may want to generate and POST a response to the command list.        */
787   /***************************************************************************/
788   if (json_ok)
789   {
790     evel_command_list_response(post);
791   }
792
793   /***************************************************************************/
794   /* Make sure we're clean on exit.                                          */
795   /***************************************************************************/
796   assert(evel_command_type_value == NULL);
797   assert(evel_measurement_interval_value == NULL);
798   assert(evel_throttle_spec_domain_value == NULL);
799   assert(evel_throttle_spec_domain == EVEL_MAX_DOMAINS);
800   assert(evel_temp_throttle == NULL);
801
802   EVEL_EXIT();
803
804   return json_ok;
805 }
806
807 /**************************************************************************//**
808  * Copy a copy of an element, in string form.
809  *
810  * The caller must manage memory allocated for the copied string.
811  *
812  * @param chunk         Memory chunk containing the JSON buffer.
813  * @param token         The token to copy from.
814  * @return the copy of the element.
815  *****************************************************************************/
816 char * evel_stack_strdup(const MEMORY_CHUNK * const chunk,
817                          const jsmntok_t * const token)
818 {
819   char temp_char;
820   char * result;
821
822   EVEL_ENTER();
823
824   /***************************************************************************/
825   /* Call strdup to copy the string, inserting a temporary \0 for the call.  */
826   /***************************************************************************/
827   temp_char = chunk->memory[token->end];
828   chunk->memory[token->end] = '\0';
829   result = strdup(chunk->memory + token->start);
830   assert(result != NULL);
831   chunk->memory[token->end] = temp_char;
832
833   EVEL_EXIT();
834
835   return result;
836 }
837
838 /**************************************************************************//**
839  * Copy a copy of an element, in string form.
840  *
841  * @param json_stack    The JSON stack to initialize.
842  * @param chunk         The underlying memory chunk used for parsing.
843  *****************************************************************************/
844 void evel_init_json_stack(EVEL_JSON_STACK * json_stack,
845                           const MEMORY_CHUNK * const chunk)
846 {
847   EVEL_JSON_STACK_ENTRY * entry;
848
849   EVEL_ENTER();
850
851   json_stack->level = 0;
852   entry = json_stack->entry;
853   entry->json_state = EVEL_JSON_ITEM;
854   entry->json_count = 0;
855   entry->num_required = 1;
856   entry->json_key = NULL;
857   json_stack->chunk = chunk;
858
859   EVEL_EXIT();
860 }
861
862 /**************************************************************************//**
863  * Push a new entry on the stack
864  *
865  * @param json_stack    The stack.
866  * @param num_required  The number of elements required.
867  * @param new_state     The state for the new entry.
868  * @return false if we cannot push onto the stack.
869  *****************************************************************************/
870 bool evel_stack_push(EVEL_JSON_STACK * const json_stack,
871                      const int num_required,
872                      const EVEL_JSON_STATE new_state)
873 {
874   EVEL_JSON_STACK_ENTRY * entry;
875   char * key;
876   bool result;
877
878   EVEL_ENTER();
879
880   /***************************************************************************/
881   /* Check preconditions.                                                    */
882   /***************************************************************************/
883   assert(json_stack != NULL);
884   assert(json_stack->level >= 0);
885   assert(json_stack->level < EVEL_JSON_STACK_DEPTH);
886   assert((new_state == EVEL_JSON_ITEM) || (new_state == EVEL_JSON_KEY));
887
888   /***************************************************************************/
889   /* Check nesting depth, and stop processing if we hit the limit.           */
890   /***************************************************************************/
891   if ((json_stack->level + 1) >= EVEL_JSON_STACK_DEPTH)
892   {
893     EVEL_ERROR("JSON Nesting is too deep - stop processing");
894     result = false;
895     goto exit_label;
896   }
897
898   /***************************************************************************/
899   /* Evaluate cases where we recurse and are interested in the contents.     */
900   /***************************************************************************/
901   entry = &json_stack->entry[json_stack->level];
902   key = entry->json_key;
903
904   /***************************************************************************/
905   /* Note that this is the key before we drop a level.                       */
906   /***************************************************************************/
907   if (key != NULL)
908   {
909     EVEL_DEBUG("Push with key: %s", key);
910
911     switch (evel_json_command_state)
912     {
913       case EVEL_JCS_START:
914         if (strcmp(key, "commandList") == 0)
915         {
916           evel_set_command_state(EVEL_JCS_COMMAND_LIST);
917         }
918         break;
919
920       case EVEL_JCS_COMMAND_LIST_ENTRY:
921         if (strcmp(key, "command") == 0)
922         {
923           evel_open_command();
924           evel_set_command_state(EVEL_JCS_COMMAND);
925         }
926         break;
927
928       case EVEL_JCS_COMMAND:
929         if (strcmp(key, "eventDomainThrottleSpecification") == 0)
930         {
931           evel_open_throttle_spec();
932           evel_set_command_state(EVEL_JCS_SPEC);
933         }
934         break;
935
936       case EVEL_JCS_SPEC:
937         if (strcmp(key, "suppressedFieldNames") == 0)
938         {
939           evel_set_command_state(EVEL_JCS_FIELD_NAMES);
940         }
941         else if (strcmp(key, "suppressedNvPairsList") == 0)
942         {
943           evel_set_command_state(EVEL_JCS_PAIRS_LIST);
944         }
945         break;
946
947       case EVEL_JCS_PAIRS_LIST_ENTRY:
948         if (strcmp(key, "suppressedNvPairNames") == 0)
949         {
950           evel_set_command_state(EVEL_JCS_NV_PAIR_NAMES);
951         }
952         break;
953
954       case EVEL_JCS_FIELD_NAMES:
955       case EVEL_JCS_PAIRS_LIST:
956       case EVEL_JCS_NV_PAIR_NAMES:
957       default:
958         EVEL_ERROR("Unexpected JSON key %s in state %d",
959                    key,
960                    evel_json_command_state);
961         break;
962     }
963   }
964   else
965   {
966     EVEL_DEBUG("Push with no key");
967
968     /*************************************************************************/
969     /* If we're pushing without a key, then we're in an array.  We switch    */
970     /* state based on the existing state and stack level.                    */
971     /*************************************************************************/
972     const int COMMAND_LIST_LEVEL = 2;
973     const int NV_PAIRS_LIST_LEVEL = 6;
974
975     if ((evel_json_command_state == EVEL_JCS_PAIRS_LIST) &&
976         (json_stack->level == NV_PAIRS_LIST_LEVEL))
977     {
978       /***********************************************************************/
979       /* We are entering an object within the "suppressedNvPairsList" array. */
980       /***********************************************************************/
981       evel_open_nv_pairs_list_entry();
982       evel_set_command_state(EVEL_JCS_PAIRS_LIST_ENTRY);
983     }
984
985     if ((evel_json_command_state == EVEL_JCS_COMMAND_LIST) &&
986         (json_stack->level == COMMAND_LIST_LEVEL))
987     {
988       /***********************************************************************/
989       /* We are entering an object within the "commandList" array.           */
990       /***********************************************************************/
991       evel_set_command_state(EVEL_JCS_COMMAND_LIST_ENTRY);
992     }
993   }
994
995   /***************************************************************************/
996   /* Push the stack and initialize the entry.                                */
997   /***************************************************************************/
998   json_stack->level++;
999   entry++;
1000   EVEL_DEBUG("Stack Push -> %d", json_stack->level);
1001   entry = &json_stack->entry[json_stack->level];
1002   entry->json_count = 0;
1003   entry->num_required = num_required;
1004   entry->json_state = new_state;
1005   entry->json_key = NULL;
1006   result = true;
1007
1008 exit_label:
1009
1010   EVEL_EXIT();
1011
1012   return result;
1013 }
1014
1015 /**************************************************************************//**
1016  * Pop any stack entries which have collected the required number of items.
1017  *
1018  * @param json_stack    The stack.
1019  *****************************************************************************/
1020 void evel_stack_pop(EVEL_JSON_STACK * const json_stack)
1021 {
1022   EVEL_JSON_STACK_ENTRY * entry;
1023   char * key;
1024
1025   EVEL_ENTER();
1026
1027   /***************************************************************************/
1028   /* Check preconditions.                                                    */
1029   /***************************************************************************/
1030   assert(json_stack != NULL);
1031   assert(json_stack->level >= 0);
1032   assert(json_stack->level < EVEL_JSON_STACK_DEPTH);
1033
1034   entry = &json_stack->entry[json_stack->level];
1035   while ((json_stack->level > 0) && (entry->json_count == entry->num_required))
1036   {
1037     key = entry->json_key;
1038
1039     switch (evel_json_command_state)
1040     {
1041       case EVEL_JCS_COMMAND_LIST:
1042         evel_set_command_state(EVEL_JCS_START);
1043         break;
1044
1045       case EVEL_JCS_COMMAND_LIST_ENTRY:
1046         evel_set_command_state(EVEL_JCS_COMMAND_LIST);
1047         break;
1048
1049       case EVEL_JCS_COMMAND:
1050         evel_close_command();
1051         evel_set_command_state(EVEL_JCS_COMMAND_LIST_ENTRY);
1052         break;
1053
1054       case EVEL_JCS_SPEC:
1055         evel_close_throttle_spec();
1056         evel_set_command_state(EVEL_JCS_COMMAND);
1057         break;
1058
1059       case EVEL_JCS_FIELD_NAMES:
1060         evel_set_command_state(EVEL_JCS_SPEC);
1061         break;
1062
1063       case EVEL_JCS_PAIRS_LIST:
1064         evel_set_command_state(EVEL_JCS_SPEC);
1065         break;
1066
1067       case EVEL_JCS_PAIRS_LIST_ENTRY:
1068         evel_close_nv_pairs_list_entry();
1069         evel_set_command_state(EVEL_JCS_PAIRS_LIST);
1070         break;
1071
1072       case EVEL_JCS_NV_PAIR_NAMES:
1073         evel_set_command_state(EVEL_JCS_PAIRS_LIST_ENTRY);
1074         break;
1075
1076       default:
1077         break;
1078     }
1079
1080     /*************************************************************************/
1081     /* Free off any key that was duplicated and stored.                      */
1082     /*************************************************************************/
1083     if (key != NULL)
1084     {
1085       free(key);
1086       entry->json_key = NULL;
1087     }
1088
1089     /*************************************************************************/
1090     /* We just reached the required number of key-value pairs or items, so   */
1091     /* pop the stack.                                                        */
1092     /*************************************************************************/
1093     json_stack->level--;
1094     entry--;
1095
1096     EVEL_DEBUG("Stack Pop  -> %d", json_stack->level);
1097
1098     /*************************************************************************/
1099     /* We just completed collection of an ITEM (within an ARRAY) or a VALUE  */
1100     /* (within an OBJECT).  Either way, we need to count it.                 */
1101     /*************************************************************************/
1102     entry->json_count++;
1103
1104     /*************************************************************************/
1105     /* If we just completed a VALUE, then we expect the next element to be a */
1106     /* key, if there is a next element.                                      */
1107     /*************************************************************************/
1108     if (entry->json_state == EVEL_JSON_VALUE)
1109     {
1110       entry->json_state = EVEL_JSON_KEY;
1111     }
1112   }
1113
1114   EVEL_EXIT();
1115 }
1116
1117 /**************************************************************************//**
1118  * Pop all stack entries, freeing any memory as we go.
1119  *
1120  * @param json_stack    The stack.
1121  *****************************************************************************/
1122 void evel_stack_cleanup(EVEL_JSON_STACK * const json_stack)
1123 {
1124   EVEL_JSON_STACK_ENTRY * entry;
1125
1126   EVEL_ENTER();
1127
1128   /***************************************************************************/
1129   /* Check preconditions.                                                    */
1130   /***************************************************************************/
1131   assert(json_stack != NULL);
1132   assert(json_stack->level >= 0);
1133   assert(json_stack->level < EVEL_JSON_STACK_DEPTH);
1134
1135   entry = &json_stack->entry[json_stack->level];
1136   while ((json_stack->level > 0))
1137   {
1138     /*************************************************************************/
1139     /* Free off any key that was duplicated and stored.                      */
1140     /*************************************************************************/
1141     if (entry->json_key != NULL)
1142     {
1143       free(entry->json_key);
1144       entry->json_key = NULL;
1145     }
1146
1147     /*************************************************************************/
1148     /* We just reached the required number of key-value pairs or items, so   */
1149     /* pop the stack.                                                        */
1150     /*************************************************************************/
1151     json_stack->level--;
1152     entry--;
1153   }
1154
1155   /***************************************************************************/
1156   /* If we hit EVEL_JSON_STACK_DEPTH, we exit the loop and can leave these   */
1157   /* values hanging - so clean them up.                                      */
1158   /***************************************************************************/
1159   if (evel_command_type_value != NULL)
1160   {
1161     free(evel_command_type_value);
1162     evel_command_type_value = NULL;
1163   }
1164   if (evel_measurement_interval_value != NULL)
1165   {
1166     free(evel_measurement_interval_value);
1167     evel_measurement_interval_value = NULL;
1168   }
1169   if (evel_throttle_spec_domain_value != NULL)
1170   {
1171     free(evel_throttle_spec_domain_value);
1172     evel_throttle_spec_domain_value = NULL;
1173   }
1174   evel_throttle_spec_domain = EVEL_MAX_DOMAINS;
1175   if (evel_temp_throttle != NULL)
1176   {
1177     evel_throttle_free(evel_temp_throttle);
1178     evel_temp_throttle = NULL;
1179   }
1180
1181   EVEL_EXIT();
1182 }
1183
1184 /**************************************************************************//**
1185  * Store a key in the JSON stack.
1186  *
1187  * We always store the most recent key at each level in the stack.
1188  *
1189  * @param json_stack    The stack.
1190  * @param token         The token holding the key.
1191  *****************************************************************************/
1192 void evel_stack_store_key(EVEL_JSON_STACK * const json_stack,
1193                           const jsmntok_t * const token)
1194 {
1195   EVEL_JSON_STACK_ENTRY * entry;
1196
1197   EVEL_ENTER();
1198
1199   /***************************************************************************/
1200   /* Check preconditions.                                                    */
1201   /***************************************************************************/
1202   assert(json_stack != NULL);
1203   assert(json_stack->level >= 0);
1204   assert(json_stack->level < EVEL_JSON_STACK_DEPTH);
1205
1206   /***************************************************************************/
1207   /* Free any previously stored key, replacing it with the new one.          */
1208   /***************************************************************************/
1209   entry = &json_stack->entry[json_stack->level];
1210   if (entry->json_key != NULL)
1211   {
1212     free(entry->json_key);
1213   }
1214   entry->json_key = evel_stack_strdup(json_stack->chunk, token);
1215
1216   /***************************************************************************/
1217   /* Switch state to collecting the corresponding value.                     */
1218   /***************************************************************************/
1219   entry->json_state = EVEL_JSON_VALUE;
1220
1221   EVEL_DEBUG("Stored key: %s", entry->json_key);
1222   EVEL_EXIT();
1223 }
1224
1225 /**************************************************************************//**
1226  * Store a value in the JSON stack.
1227  *
1228  * @param json_stack    The stack.
1229  * @param token         The token holding the value.
1230  *****************************************************************************/
1231 void evel_stack_store_value(EVEL_JSON_STACK * const json_stack,
1232                             const jsmntok_t * const token)
1233 {
1234   EVEL_JSON_STACK_ENTRY * entry;
1235   char * value;
1236   bool stored;
1237
1238   EVEL_ENTER();
1239
1240   /***************************************************************************/
1241   /* Check preconditions.                                                    */
1242   /***************************************************************************/
1243   assert(json_stack != NULL);
1244   assert(json_stack->level >= 0);
1245   assert(json_stack->level < EVEL_JSON_STACK_DEPTH);
1246
1247   /***************************************************************************/
1248   /* Based on the (key, state), work out whether we're expecting a value,    */
1249   /* then store or ignore it as required.                                    */
1250   /***************************************************************************/
1251   entry = &json_stack->entry[json_stack->level];
1252   value = evel_stack_strdup(json_stack->chunk, token);
1253   stored = false;
1254   EVEL_DEBUG("Store value: %s", value);
1255
1256   switch (evel_json_command_state)
1257   {
1258     case EVEL_JCS_COMMAND:
1259       if (strcmp(entry->json_key, "commandType") == 0)
1260       {
1261         evel_command_type_value = value;
1262         stored = true;
1263       }
1264       else if (strcmp(entry->json_key, "measurementInterval") == 0)
1265       {
1266         evel_measurement_interval_value = value;
1267         stored = true;
1268       }
1269       break;
1270
1271     case EVEL_JCS_SPEC:
1272       if (strcmp(entry->json_key, "eventDomain") == 0)
1273       {
1274         evel_throttle_spec_domain_value = value;
1275         stored = true;
1276       }
1277       break;
1278
1279     case EVEL_JCS_PAIRS_LIST_ENTRY:
1280       if (strcmp(entry->json_key, "nvPairFieldName") == 0)
1281       {
1282         evel_store_nv_pair_field_name(value);
1283         stored = true;
1284       }
1285       break;
1286
1287     default:
1288       EVEL_DEBUG("Ignoring value in state: %s",
1289                  evel_jcs_strings[evel_json_command_state]);
1290       break;
1291   }
1292
1293   if (!stored)
1294   {
1295     EVEL_DEBUG("Ignored value: %s", value);
1296     free(value);
1297   }
1298
1299   /***************************************************************************/
1300   /* Switch state to another key.                                            */
1301   /***************************************************************************/
1302   entry->json_state = EVEL_JSON_KEY;
1303
1304   /***************************************************************************/
1305   /* Count the key-value pair.                                               */
1306   /***************************************************************************/
1307   entry->json_count++;
1308
1309   EVEL_EXIT();
1310 }
1311
1312 /**************************************************************************//**
1313  * Store an item in the JSON stack - a string or primitive in an array.
1314  *
1315  * @param json_stack    The stack.
1316  * @param token         The token holding the item.
1317  *****************************************************************************/
1318 void evel_stack_store_item(EVEL_JSON_STACK * const json_stack,
1319                            const jsmntok_t * const token)
1320 {
1321   EVEL_JSON_STACK_ENTRY * entry;
1322   char * item;
1323   bool stored;
1324
1325   EVEL_ENTER();
1326
1327   /***************************************************************************/
1328   /* Check preconditions.                                                    */
1329   /***************************************************************************/
1330   assert(json_stack != NULL);
1331   assert(json_stack->level >= 0);
1332   assert(json_stack->level < EVEL_JSON_STACK_DEPTH);
1333
1334   /***************************************************************************/
1335   /* Based on the state, work out whether we're expecting an item, then      */
1336   /* store or ignore it as required.                                         */
1337   /***************************************************************************/
1338   entry = &json_stack->entry[json_stack->level];
1339   item = evel_stack_strdup(json_stack->chunk, token);
1340   stored = false;
1341   EVEL_DEBUG("Store item: %s", item);
1342
1343   switch (evel_json_command_state)
1344   {
1345     case EVEL_JCS_NV_PAIR_NAMES:
1346       evel_store_nv_pair_name(item);
1347       stored = true;
1348       break;
1349
1350     case EVEL_JCS_FIELD_NAMES:
1351       evel_store_suppressed_field_name(item);
1352       stored = true;
1353       break;
1354
1355     default:
1356       EVEL_DEBUG("Ignoring item in state: %s",
1357                  evel_jcs_strings[evel_json_command_state]);
1358       break;
1359   }
1360
1361   if (!stored)
1362   {
1363     EVEL_DEBUG("Ignored item: %s", item);
1364     free(item);
1365   }
1366
1367   /***************************************************************************/
1368   /* We need another item.  This is purely defensive.                        */
1369   /***************************************************************************/
1370   entry->json_state = EVEL_JSON_ITEM;
1371
1372   /***************************************************************************/
1373   /* Count the item.                                                         */
1374   /***************************************************************************/
1375   entry->json_count++;
1376 }
1377
1378 /**************************************************************************//**
1379  * Set the JSON command state to a new value.
1380  *
1381  * @param new_state     The new state to set.
1382  *****************************************************************************/
1383 void evel_set_command_state(const EVEL_JSON_COMMAND_STATE new_state)
1384 {
1385   EVEL_ENTER();
1386
1387   /***************************************************************************/
1388   /* Check preconditions.                                                    */
1389   /***************************************************************************/
1390   assert(evel_json_command_state < EVEL_JCS_MAX);
1391   assert(new_state < EVEL_JCS_MAX);
1392
1393   /***************************************************************************/
1394   /* Provide common debug, and set the new state.                            */
1395   /***************************************************************************/
1396   EVEL_DEBUG("Command State: %s -> %s",
1397              evel_jcs_strings[evel_json_command_state],
1398              evel_jcs_strings[new_state]);
1399   evel_json_command_state = new_state;
1400
1401   EVEL_EXIT();
1402 }
1403
1404 /**************************************************************************//**
1405  * Produce debug output from a JSON token.
1406  *
1407  * @param chunk         Memory chunk containing the JSON buffer.
1408  * @param token         Token to dump.
1409  *****************************************************************************/
1410 void evel_debug_token(const MEMORY_CHUNK * const chunk,
1411                       const jsmntok_t * const token)
1412 {
1413   char temp_char;
1414
1415   EVEL_ENTER();
1416
1417   /***************************************************************************/
1418   /* Check preconditions.                                                    */
1419   /***************************************************************************/
1420   assert(token->type > 0);
1421   assert(token->type < JSON_TOKEN_TYPES);
1422
1423   /***************************************************************************/
1424   /* Log the token, leaving it in the state in which it started.             */
1425   /***************************************************************************/
1426   temp_char = chunk->memory[token->end];
1427   chunk->memory[token->end] = '\0';
1428   EVEL_DEBUG("JSON token type: %s", evel_json_token_strings[token->type]);
1429   EVEL_DEBUG("JSON token: %s", chunk->memory + token->start);
1430   chunk->memory[token->end] = temp_char;
1431
1432   EVEL_EXIT();
1433 }
1434
1435 /**************************************************************************//**
1436  * Post a response to the commandList.
1437  *
1438  * @param post          Memory chunk in which to post a response.
1439  *****************************************************************************/
1440 void evel_command_list_response(MEMORY_CHUNK * const post)
1441 {
1442   char * json_post;
1443
1444   EVEL_ENTER();
1445
1446   /***************************************************************************/
1447   /* Check preconditions.                                                    */
1448   /***************************************************************************/
1449   assert(post != NULL);
1450   assert(post->memory == NULL);
1451
1452   if (evel_provide_throttling_state)
1453   {
1454     EVEL_DEBUG("Provide throttling state");
1455
1456     /*************************************************************************/
1457     /* Encode the response, making it printf-able for debug.                 */
1458     /*************************************************************************/
1459     json_post = malloc(EVEL_MAX_JSON_BODY);
1460     assert(json_post != NULL);
1461     post->size = evel_json_encode_throttle(json_post, EVEL_MAX_JSON_BODY - 1);
1462     post->memory = json_post;
1463     post->memory[post->size] = '\0';
1464     evel_provide_throttling_state = false;
1465   }
1466
1467   EVEL_EXIT();
1468 }
1469
1470 /**************************************************************************//**
1471  * Encode the full throttling specification according to AT&T's schema.
1472  *
1473  * @param json          Pointer to where to store the JSON encoded data.
1474  * @param max_size      Size of storage available in json_body.
1475  * @returns Number of bytes actually written.
1476  *****************************************************************************/
1477 int evel_json_encode_throttle(char * const json, const int max_size)
1478 {
1479   bool throttled;
1480   int domain;
1481   int offset;
1482   bool domain_added;
1483
1484   EVEL_ENTER();
1485
1486   /***************************************************************************/
1487   /* Check preconditions.                                                    */
1488   /***************************************************************************/
1489   assert(json != NULL);
1490   assert(max_size > 0);
1491
1492   /***************************************************************************/
1493   /* Work out if we're throttled.                                            */
1494   /***************************************************************************/
1495   throttled = false;
1496   for (domain = EVEL_DOMAIN_FAULT; domain < EVEL_MAX_DOMAINS; domain++)
1497   {
1498     if (evel_throttle_spec[domain] != NULL)
1499     {
1500       throttled = true;
1501     }
1502   }
1503
1504   /***************************************************************************/
1505   /* Encode the response.                                                    */
1506   /***************************************************************************/
1507   offset = 0;
1508   offset += snprintf(json + offset, max_size - offset,
1509                      "{\"eventThrottlingState\": {");
1510   offset += snprintf(json + offset, max_size - offset,
1511                      "\"eventThrottlingMode\": \"%s\"",
1512                      throttled ? "throttled" : "normal");
1513   if (throttled)
1514   {
1515     offset += snprintf(json + offset, max_size - offset,
1516                        ", \"eventDomainThrottleSpecificationList\": [");
1517
1518     domain_added = false;
1519     for (domain = EVEL_DOMAIN_FAULT; domain < EVEL_MAX_DOMAINS; domain++)
1520     {
1521       if (evel_throttle_spec[domain] != NULL)
1522       {
1523         if (domain_added)
1524         {
1525           offset += snprintf(json + offset, max_size - offset, ", ");
1526         }
1527
1528         offset += evel_json_encode_throttle_spec(json + offset,
1529                                                  max_size - offset,
1530                                                  domain);
1531         domain_added = true;
1532       }
1533     }
1534
1535     offset += snprintf(json + offset, max_size - offset, "]");
1536   }
1537
1538   offset += snprintf(json + offset, max_size - offset, "}}");
1539
1540   EVEL_EXIT();
1541
1542   return offset;
1543 }
1544
1545 /**************************************************************************//**
1546  * Encode a throttling specification for a domain.
1547  *
1548  * @param json          Pointer to where to store the JSON encoded data.
1549  * @param max_size      Size of storage available in json_body.
1550  * @returns Number of bytes actually written.
1551  *****************************************************************************/
1552 int evel_json_encode_throttle_spec(char * const json,
1553                                    const int max_size,
1554                                    const EVEL_EVENT_DOMAINS domain)
1555 {
1556   int offset;
1557   EVEL_THROTTLE_SPEC * throttle_spec;
1558   DLIST_ITEM * dlist_item;
1559   EVEL_ENTER();
1560
1561   /***************************************************************************/
1562   /* Check preconditions.                                                    */
1563   /***************************************************************************/
1564   assert(domain >= EVEL_DOMAIN_FAULT);
1565   assert(domain < EVEL_MAX_DOMAINS);
1566   assert(evel_throttle_spec[domain] != NULL);
1567
1568   throttle_spec = evel_throttle_spec[domain];
1569
1570   /***************************************************************************/
1571   /* Encode the domain.                                                      */
1572   /***************************************************************************/
1573   offset = 0;
1574   offset += snprintf(json + offset, max_size - offset,
1575                      "{");
1576   offset += snprintf(json + offset, max_size - offset,
1577                      "\"eventDomain\": \"%s\"",
1578                      evel_domain_strings[domain]);
1579
1580   /***************************************************************************/
1581   /* Encode "suppressedFieldNames".                                          */
1582   /***************************************************************************/
1583   dlist_item = dlist_get_first(&throttle_spec->suppressed_field_names);
1584   if (dlist_item != NULL)
1585   {
1586     offset += snprintf(json + offset, max_size - offset,
1587                        ", \"suppressedFieldNames\": [");
1588     while (dlist_item != NULL)
1589     {
1590       char * suppressed_field = dlist_item->item;
1591       assert(suppressed_field != NULL);
1592
1593       offset += snprintf(json + offset, max_size - offset,
1594                          "\"%s\"", suppressed_field);
1595       dlist_item = dlist_get_next(dlist_item);
1596       if (dlist_item != NULL)
1597       {
1598         offset += snprintf(json + offset, max_size - offset, ", ");
1599       }
1600     }
1601
1602     offset += snprintf(json + offset, max_size - offset, "]");
1603   }
1604
1605   /***************************************************************************/
1606   /* Encode "suppressedNvPairsList".                                         */
1607   /***************************************************************************/
1608   dlist_item = dlist_get_first(&throttle_spec->suppressed_nv_pairs_list);
1609   if (dlist_item != NULL)
1610   {
1611     offset += snprintf(json + offset, max_size - offset,
1612                        ", \"suppressedNvPairsList\": [");
1613     while (dlist_item != NULL)
1614     {
1615       offset += evel_json_encode_nv_pairs(json + offset,
1616                                           max_size - offset,
1617                                           dlist_item->item);
1618       dlist_item = dlist_get_next(dlist_item);
1619       if (dlist_item != NULL)
1620       {
1621         offset += snprintf(json + offset, max_size - offset, ", ");
1622       }
1623     }
1624
1625     offset += snprintf(json + offset, max_size - offset, "]");
1626   }
1627
1628   offset += snprintf(json + offset, max_size - offset, "}");
1629
1630   EVEL_EXIT();
1631
1632   return offset;
1633 }
1634
1635 /**************************************************************************//**
1636  * Encode a single "suppressedNvPairsListEntry".
1637  *
1638  * @param json          Pointer to where to store the JSON encoded data.
1639  * @param max_size      Size of storage available in json_body.
1640  * @returns Number of bytes actually written.
1641  *****************************************************************************/
1642 int evel_json_encode_nv_pairs(char * const json,
1643                               const int max_size,
1644                               EVEL_SUPPRESSED_NV_PAIRS * nv_pairs)
1645 {
1646   DLIST_ITEM * dlist_item;
1647   char * name;
1648   int offset;
1649
1650   /***************************************************************************/
1651   /* Check preconditions.                                                    */
1652   /***************************************************************************/
1653   assert(nv_pairs != NULL);
1654   assert(nv_pairs->nv_pair_field_name != NULL);
1655   assert(!dlist_is_empty(&nv_pairs->suppressed_nv_pair_names));
1656
1657   /***************************************************************************/
1658   /* Encode it.                                                              */
1659   /***************************************************************************/
1660   offset = 0;
1661   offset += snprintf(json + offset, max_size - offset, "{");
1662   offset += snprintf(json + offset, max_size - offset,
1663                      "\"nvPairFieldName\": \"%s\"",
1664                      nv_pairs->nv_pair_field_name);
1665   dlist_item = dlist_get_first(&nv_pairs->suppressed_nv_pair_names);
1666   offset += snprintf(json + offset, max_size - offset,
1667                      ", \"suppressedNvPairNames\": [");
1668   while (dlist_item != NULL)
1669   {
1670     name = dlist_item->item;
1671     assert(name != NULL);
1672     offset += snprintf(json + offset, max_size - offset, "\"%s\"", name);
1673     dlist_item = dlist_get_next(dlist_item);
1674     if (dlist_item != NULL)
1675     {
1676       offset += snprintf(json + offset, max_size - offset, ", ");
1677     }
1678   }
1679   offset += snprintf(json + offset, max_size - offset, "]");
1680   offset += snprintf(json + offset, max_size - offset, "}");
1681
1682   EVEL_EXIT();
1683
1684   return offset;
1685 }
1686
1687 /**************************************************************************//**
1688  * Method called when we open a "command" object.
1689  *****************************************************************************/
1690 void evel_open_command()
1691 {
1692   EVEL_ENTER();
1693
1694   /***************************************************************************/
1695   /* Make some assertions.                                                   */
1696   /***************************************************************************/
1697   assert(evel_command_type_value == NULL);
1698   assert(evel_measurement_interval_value == NULL);
1699
1700   EVEL_EXIT();
1701 }
1702
1703 /**************************************************************************//**
1704  * Method called when we close a "command" object.
1705  *****************************************************************************/
1706 void evel_close_command()
1707 {
1708   EVEL_ENTER();
1709
1710   /***************************************************************************/
1711   /* If a commandType was provided, fan out and handle it now what we have   */
1712   /* fathered all related information.                                       */
1713   /*                                                                         */
1714   /* Note that we handle throttling specification and measurement interval   */
1715   /* updates immediately on closing the command (not the list). We could     */
1716   /* reject *all* commands in a list if any of them are invalid, but we are  */
1717   /* take a best-effort strategy here - any valid-looking command gets       */
1718   /* implemented regardless of what follows.                                 */
1719   /***************************************************************************/
1720   if (evel_command_type_value != NULL)
1721   {
1722     EVEL_DEBUG("Closing command %s", evel_command_type_value);
1723
1724     if (strcmp(evel_command_type_value, "provideThrottlingState") == 0)
1725     {
1726       evel_provide_throttling_state = true;
1727     }
1728     else if (strcmp(evel_command_type_value, "throttlingSpecification") == 0)
1729     {
1730       evel_set_throttling_spec();
1731     }
1732     else if (strcmp(evel_command_type_value, "measurementIntervalChange") == 0)
1733     {
1734       evel_set_measurement_interval();
1735     }
1736     else
1737     {
1738       EVEL_ERROR("Ignoring unknown commandType: %s\n",
1739                  evel_command_type_value);
1740     }
1741
1742     /*************************************************************************/
1743     /* Free the captured "commandType" value.                                */
1744     /*************************************************************************/
1745     free(evel_command_type_value);
1746     evel_command_type_value = NULL;
1747   }
1748
1749   /***************************************************************************/
1750   /* There could be an unused working throttle spec at this point - if the   */
1751   /* "throttlingSpecification" commandType was not provided, or an invalid   */
1752   /* domain was provided, or was not provided at all.                        */
1753   /***************************************************************************/
1754   if (evel_temp_throttle != NULL)
1755   {
1756     evel_throttle_free(evel_temp_throttle);
1757     evel_temp_throttle = NULL;
1758   }
1759
1760   /***************************************************************************/
1761   /* Similarly, the domain could be set.                                     */
1762   /***************************************************************************/
1763   evel_throttle_spec_domain = EVEL_MAX_DOMAINS;
1764
1765   /***************************************************************************/
1766   /* There could be an unused measurement interval value at this point - if  */
1767   /* the "measurementIntervalChange" command was not provided.               */
1768   /***************************************************************************/
1769   if (evel_measurement_interval_value != NULL)
1770   {
1771     free(evel_measurement_interval_value);
1772     evel_measurement_interval_value = NULL;
1773   }
1774
1775   EVEL_EXIT();
1776 }
1777
1778 /**************************************************************************//**
1779  * Set the provided throttling specification, when the command closes.
1780  *****************************************************************************/
1781 void evel_set_throttling_spec()
1782 {
1783   EVEL_ENTER();
1784
1785   if ((evel_throttle_spec_domain >= 0) &&
1786       (evel_throttle_spec_domain < EVEL_MAX_DOMAINS))
1787   {
1788     EVEL_DEBUG("Updating throttle spec for domain: %s",
1789                evel_domain_strings[evel_throttle_spec_domain]);
1790
1791     /*************************************************************************/
1792     /* Free off the previous throttle specification for the domain, if there */
1793     /* is one.                                                               */
1794     /*************************************************************************/
1795     if (evel_throttle_spec[evel_throttle_spec_domain] != NULL)
1796     {
1797       evel_throttle_free(evel_throttle_spec[evel_throttle_spec_domain]);
1798     }
1799
1800     /*************************************************************************/
1801     /* Finalize the working throttling spec, if there is one.                */
1802     /*************************************************************************/
1803     if (evel_temp_throttle != NULL)
1804     {
1805       evel_throttle_finalize(evel_temp_throttle);
1806     }
1807
1808     /*************************************************************************/
1809     /* Replace the throttle specification for the domain with the working    */
1810     /* throttle specification.  This could be NULL, if an empty throttle     */
1811     /* specification has been received for a domain.                         */
1812     /*************************************************************************/
1813     evel_throttle_spec[evel_throttle_spec_domain] = evel_temp_throttle;
1814     evel_temp_throttle = NULL;
1815   }
1816
1817   EVEL_EXIT();
1818 }
1819
1820 /**************************************************************************//**
1821  * Set the provided measurement interval, when the command closes.
1822  *****************************************************************************/
1823 void evel_set_measurement_interval()
1824 {
1825   EVEL_ENTER();
1826
1827   if (evel_measurement_interval_value != NULL)
1828   {
1829     const long int value = strtol(evel_measurement_interval_value, NULL, 10);
1830
1831     if ((value >= 0) && (value <= INT_MAX))
1832     {
1833       /***********************************************************************/
1834       /* Lock, update, unlock.                                               */
1835       /***********************************************************************/
1836       EVEL_DEBUG("Updating measurement interval to %d\n", value);
1837
1838       pthread_mutex_lock(&evel_measurement_interval_mutex);
1839       evel_measurement_interval = value;
1840       pthread_mutex_unlock(&evel_measurement_interval_mutex);
1841     }
1842     else
1843     {
1844       EVEL_ERROR("Ignoring invalid measurement interval: %s",
1845                  evel_measurement_interval_value);
1846     }
1847   }
1848
1849   EVEL_EXIT();
1850 }
1851
1852 /**************************************************************************//**
1853  * Method called when we open an "eventDomainThrottleSpecification" object.
1854  *****************************************************************************/
1855 void evel_open_throttle_spec()
1856 {
1857   EVEL_ENTER();
1858
1859   /***************************************************************************/
1860   /* Check preconditions.                                                    */
1861   /***************************************************************************/
1862   assert(evel_throttle_spec_domain_value == NULL);
1863   assert(evel_throttle_spec_domain == EVEL_MAX_DOMAINS);
1864   assert(evel_temp_throttle == NULL);
1865
1866   /***************************************************************************/
1867   /* Allocate and initialize an ::EVEL_THROTTLE_SPEC in which to hold        */
1868   /* captured JSON elements.                                                 */
1869   /***************************************************************************/
1870   evel_temp_throttle = malloc(sizeof(EVEL_THROTTLE_SPEC));
1871   assert(evel_temp_throttle != NULL);
1872   dlist_initialize(&evel_temp_throttle->suppressed_field_names);
1873   dlist_initialize(&evel_temp_throttle->suppressed_nv_pairs_list);
1874   evel_temp_throttle->hash_field_names = NULL;
1875   evel_temp_throttle->hash_nv_pairs_list = NULL;
1876
1877   EVEL_EXIT();
1878 }
1879
1880 /**************************************************************************//**
1881  * Method called when we close an "eventDomainThrottleSpecification" object.
1882  *****************************************************************************/
1883 void evel_close_throttle_spec()
1884 {
1885   EVEL_ENTER();
1886
1887   /***************************************************************************/
1888   /* Decode, free and blank a captured event domain value.                   */
1889   /***************************************************************************/
1890   if (evel_throttle_spec_domain_value != NULL)
1891   {
1892     evel_throttle_spec_domain =
1893                            evel_decode_domain(evel_throttle_spec_domain_value);
1894     free(evel_throttle_spec_domain_value);
1895     evel_throttle_spec_domain_value = NULL;
1896   }
1897
1898   /***************************************************************************/
1899   /* Free off an empty working throttle spec, to stop it being used.  This   */
1900   /* state should be represented by a NULL pointer for the domain.           */
1901   /***************************************************************************/
1902   if (evel_temp_throttle != NULL)
1903   {
1904     if (dlist_is_empty(&evel_temp_throttle->suppressed_field_names) &&
1905         dlist_is_empty(&evel_temp_throttle->suppressed_nv_pairs_list))
1906     {
1907       free(evel_temp_throttle);
1908       evel_temp_throttle = NULL;
1909     }
1910   }
1911
1912   EVEL_EXIT();
1913 }
1914
1915 /**************************************************************************//**
1916  * Convert a value for an "eventDomain" into an ::EVEL_EVENT_DOMAINS.
1917  *
1918  * @param domain_value  The domain string value to decode.
1919  * @returns The matching ::EVEL_EVENT_DOMAINS, or ::EVEL_MAX_DOMAINS on error.
1920  *****************************************************************************/
1921 EVEL_EVENT_DOMAINS evel_decode_domain(char * domain_value)
1922 {
1923   EVEL_EVENT_DOMAINS result;
1924   int ii;
1925
1926   EVEL_ENTER();
1927
1928   /***************************************************************************/
1929   /* Check preconditions.                                                    */
1930   /***************************************************************************/
1931   assert(domain_value != NULL);
1932
1933   result = EVEL_MAX_DOMAINS;
1934   for (ii = EVEL_DOMAIN_FAULT; ii < EVEL_MAX_DOMAINS; ii++)
1935   {
1936     assert(evel_domain_strings[ii] != NULL);
1937     if (strcmp(evel_domain_strings[ii], domain_value) == 0)
1938     {
1939       result = ii;
1940     }
1941   }
1942
1943   EVEL_EXIT();
1944
1945   return result;
1946 }
1947
1948 /**************************************************************************//**
1949  * Method called when we open a "suppressedNvPairsListEntry" object.
1950  *****************************************************************************/
1951 void evel_open_nv_pairs_list_entry()
1952 {
1953   EVEL_SUPPRESSED_NV_PAIRS * nv_pairs;
1954
1955   EVEL_ENTER();
1956
1957   /***************************************************************************/
1958   /* Check preconditions.                                                    */
1959   /***************************************************************************/
1960   assert(evel_temp_throttle != NULL);
1961
1962   /***************************************************************************/
1963   /* Allocate and initialize an ::EVEL_SUPPRESSED_NV_PAIRS, and add it to    */
1964   /* the list.                                                               */
1965   /***************************************************************************/
1966   nv_pairs = malloc(sizeof(EVEL_SUPPRESSED_NV_PAIRS));
1967   assert(nv_pairs != NULL);
1968   nv_pairs->nv_pair_field_name = NULL;
1969   dlist_initialize(&nv_pairs->suppressed_nv_pair_names);
1970   nv_pairs->hash_nv_pair_names = NULL;
1971   dlist_push_last(&evel_temp_throttle->suppressed_nv_pairs_list, nv_pairs);
1972
1973   EVEL_EXIT();
1974 }
1975
1976 /**************************************************************************//**
1977  * Method called when we close a "suppressedNvPairsListEntry" object.
1978  *****************************************************************************/
1979 void evel_close_nv_pairs_list_entry()
1980 {
1981   EVEL_SUPPRESSED_NV_PAIRS * nv_pairs;
1982   EVEL_SUPPRESSED_NV_PAIRS * popped;
1983
1984   EVEL_ENTER();
1985
1986   /***************************************************************************/
1987   /* Get the latest nv pairs.  This also performs the required checks.       */
1988   /***************************************************************************/
1989   nv_pairs = evel_get_last_nv_pairs();
1990
1991   /***************************************************************************/
1992   /* For a "suppressedNvPairsListEntry" to have any meaning, we need both    */
1993   /* "nvPairFieldName" and "suppressedNvPairNames".  If we don't, then pop   */
1994   /* and free whatever we just collected.                                    */
1995   /***************************************************************************/
1996   if ((nv_pairs->nv_pair_field_name == NULL) ||
1997       dlist_is_empty(&nv_pairs->suppressed_nv_pair_names))
1998   {
1999     popped = dlist_pop_last(&evel_temp_throttle->suppressed_nv_pairs_list);
2000     assert(popped == nv_pairs);
2001     evel_throttle_free_nv_pair(popped);
2002   }
2003
2004   EVEL_EXIT();
2005 }
2006
2007 /**************************************************************************//**
2008  * Store an "nvPairFieldName" value in the working throttle spec.
2009  *
2010  * @param value         The value to store.
2011  *****************************************************************************/
2012 void evel_store_nv_pair_field_name(char * const value)
2013 {
2014   EVEL_SUPPRESSED_NV_PAIRS * nv_pairs;
2015
2016   EVEL_ENTER();
2017
2018   /***************************************************************************/
2019   /* Get the latest nv pairs.  This also performs the required checks.       */
2020   /***************************************************************************/
2021   nv_pairs = evel_get_last_nv_pairs();
2022
2023   /***************************************************************************/
2024   /* Store the value.                                                        */
2025   /***************************************************************************/
2026   nv_pairs->nv_pair_field_name = value;
2027
2028   EVEL_EXIT();
2029 }
2030
2031 /**************************************************************************//**
2032  * Store a "suppressedNvPairNames" item in the working throttle spec.
2033  *
2034  * @param item          The item to store.
2035  *****************************************************************************/
2036 void evel_store_nv_pair_name(char * const item)
2037 {
2038   EVEL_SUPPRESSED_NV_PAIRS * nv_pairs;
2039
2040   EVEL_ENTER();
2041
2042   /***************************************************************************/
2043   /* Get the latest nv pairs.  This also performs the required checks.       */
2044   /***************************************************************************/
2045   nv_pairs = evel_get_last_nv_pairs();
2046
2047   /***************************************************************************/
2048   /* Store the item.                                                         */
2049   /***************************************************************************/
2050   dlist_push_last(&nv_pairs->suppressed_nv_pair_names, item);
2051
2052   EVEL_EXIT();
2053 }
2054
2055 /**************************************************************************//**
2056  * Store a "suppressedFieldNames" item in the working throttle spec.
2057  *
2058  * @param item          The item to store.
2059  *****************************************************************************/
2060 void evel_store_suppressed_field_name(char * const item)
2061 {
2062   EVEL_ENTER();
2063
2064   /***************************************************************************/
2065   /* Check preconditions.                                                    */
2066   /***************************************************************************/
2067   assert(evel_temp_throttle != NULL);
2068
2069   /***************************************************************************/
2070   /* Store the item.                                                         */
2071   /***************************************************************************/
2072   dlist_push_last(&evel_temp_throttle->suppressed_field_names, item);
2073
2074   EVEL_EXIT();
2075 }
2076
2077 /**************************************************************************//**
2078  * Get the last added suppressed nv pairs list entry in the working spec.
2079  *
2080  * @returns The last entry.
2081  *****************************************************************************/
2082 EVEL_SUPPRESSED_NV_PAIRS * evel_get_last_nv_pairs()
2083 {
2084   DLIST_ITEM * dlist_item;
2085   EVEL_SUPPRESSED_NV_PAIRS * nv_pairs;
2086
2087   EVEL_ENTER();
2088
2089   /***************************************************************************/
2090   /* Check preconditions.                                                    */
2091   /***************************************************************************/
2092   assert(evel_temp_throttle != NULL);
2093
2094   /***************************************************************************/
2095   /* Get the pair that was added when we opened the list entry.              */
2096   /***************************************************************************/
2097   dlist_item = dlist_get_last(&evel_temp_throttle->suppressed_nv_pairs_list);
2098   assert(dlist_item != NULL);
2099   nv_pairs = dlist_item->item;
2100   assert(nv_pairs != NULL);
2101
2102   EVEL_EXIT();
2103
2104   return nv_pairs;
2105 }