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