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