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