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