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