d10543b0907e429dc0a8e1c8b0280c8a4b800227
[demo.git] / vnfs / VES5.0 / evel / evel-library / code / evel_library / evel_event_mgr.c
1 /*************************************************************************//**
2  *
3  * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
4  *
5  * Unless otherwise specified, all software contained herein is
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and 
15  * limitations under the License.
16  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
17  ****************************************************************************/
18
19 /**************************************************************************//**
20  * @file
21  * Event Manager
22  *
23  * Simple event manager that is responsible for taking events (Heartbeats,
24  * Faults and Measurements) from the ring-buffer and posting them to the API.
25  *
26  ****************************************************************************/
27
28 #include <string.h>
29 #include <assert.h>
30 #include <stdlib.h>
31 #include <pthread.h>
32
33 #include <curl/curl.h>
34
35 #include "evel.h"
36 #include "evel_internal.h"
37 #include "ring_buffer.h"
38 #include "evel_throttle.h"
39
40 /**************************************************************************//**
41  * How long we're prepared to wait for the API service to respond in
42  * seconds.
43  *****************************************************************************/
44 static const int EVEL_API_TIMEOUT = 5;
45
46 /*****************************************************************************/
47 /* Prototypes of locally scoped functions.                                   */
48 /*****************************************************************************/
49 static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp);
50 static void * event_handler(void *arg);
51 static bool evel_handle_response_tokens(const MEMORY_CHUNK * const chunk,
52                                         const jsmntok_t * const json_tokens,
53                                         const int num_tokens,
54                                         MEMORY_CHUNK * const post);
55 static bool evel_tokens_match_command_list(const MEMORY_CHUNK * const chunk,
56                                            const jsmntok_t * const json_token,
57                                            const int num_tokens);
58 static bool evel_token_equals_string(const MEMORY_CHUNK * const chunk,
59                                      const jsmntok_t * const json_token,
60                                      const char * check_string);
61
62 /**************************************************************************//**
63  * Buffers for error strings from libcurl.
64  *****************************************************************************/
65 static char curl_err_string[CURL_ERROR_SIZE] = "<NULL>";
66
67 /**************************************************************************//**
68  * Handle for the API into libcurl.
69  *****************************************************************************/
70 static CURL * curl_handle = NULL;
71
72 /**************************************************************************//**
73  * Special headers that we send.
74  *****************************************************************************/
75 static struct curl_slist * hdr_chunk = NULL;
76
77 /**************************************************************************//**
78  * Message queue for sending events to the API.
79  *****************************************************************************/
80 static ring_buffer event_buffer;
81
82 /**************************************************************************//**
83  * Single pending priority post, which can be generated as a result of a
84  * response to an event.  Currently only used to respond to a commandList.
85  *****************************************************************************/
86 static MEMORY_CHUNK priority_post;
87
88 /**************************************************************************//**
89  * The thread which is responsible for handling events off of the ring-buffer
90  * and posting them to the Event Handler API.
91  *****************************************************************************/
92 static pthread_t evt_handler_thread;
93
94 /**************************************************************************//**
95  * Variable to convey to the event handler thread what the foreground wants it
96  * to do.
97  *****************************************************************************/
98 static EVT_HANDLER_STATE evt_handler_state = EVT_HANDLER_UNINITIALIZED;
99
100 /**************************************************************************//**
101  * The configured API URL for event and throttling.
102  *****************************************************************************/
103 static char * evel_event_api_url;
104 static char * evel_throt_api_url;
105 static char * evel_batch_api_url;
106
107 /**************************************************************************//**
108  * Initialize the event handler.
109  *
110  * Primarily responsible for getting CURL ready for use.
111  *
112  * @param[in] event_api_url
113  *                      The URL where the Vendor Event Listener API is expected
114  *                      to be.
115  * @param[in] throt_api_url
116  *                      The URL where the Throttling API is expected to be.
117  * @param[in] source_ip  Source IP of VES Agent
118  * @param[in] ring_buf_size     Initial size of ring buffer
119  * @param[in] secure     Whether Using http or https
120  * @param[in] cert_file_path  Path to Client Certificate file
121  * @param[in] key_file_path   Path to Client key file
122  * @param[in] ca_info         Path to CA info file
123  * @param[in] ca_file_path    Path to CA file 
124  * @param[in] verify_peer     Using peer verification or not 0 or 1
125  * @param[in] verify_host     Using host verification or not 0 or 1
126  * @param[in] username  The username for the Basic Authentication of requests.
127  * @param[in] password  The password for the Basic Authentication of requests.
128  * @param     verbosity 0 for normal operation, positive values for chattier
129  *                        logs.
130  *****************************************************************************/
131 EVEL_ERR_CODES event_handler_initialize(const char * const event_api_url,
132                                         const char * const throt_api_url,
133                                         const char * const source_ip,
134                                         int ring_buf_size,
135                                         int secure,
136                                         const char * const cert_file_path,
137                                         const char * const key_file_path,
138                                         const char * const ca_info,
139                                         const char * const ca_file_path,
140                                         long verify_peer,
141                                         long verify_host,
142                                         const char * const username,
143                                         const char * const password,
144                                         int verbosity)
145 {
146   int rc = EVEL_SUCCESS;
147   CURLcode curl_rc = CURLE_OK;
148   char batch_api_url[EVEL_MAX_URL_LEN + 1] = {0};
149   char local_address[64];
150
151   EVEL_ENTER();
152
153   /***************************************************************************/
154   /* Check assumptions.                                                      */
155   /***************************************************************************/
156   assert(event_api_url != NULL);
157   assert(throt_api_url != NULL);
158   assert(username != NULL);
159   assert(password != NULL);
160
161   /***************************************************************************/
162   /* Store the API URLs.                                                     */
163   /***************************************************************************/
164   evel_event_api_url = strdup(event_api_url);
165   assert(evel_event_api_url != NULL);
166   sprintf(batch_api_url,"%s/eventBatch",event_api_url);
167   evel_batch_api_url = strdup(batch_api_url);
168   assert(evel_batch_api_url != NULL);
169   evel_throt_api_url = strdup(throt_api_url);
170   assert(evel_throt_api_url != NULL);
171
172
173   curl_version_info_data *d = curl_version_info(CURLVERSION_NOW);
174   /* compare with the 24 bit hex number in 8 bit fields */
175   if(d->version_num >= 0x072100) {
176      /* this is libcurl 7.33.0 or later */
177      EVEL_INFO("7.33 or later Curl version %x.",d->version_num);
178   }
179   else {
180      EVEL_INFO("Old Curl version.");
181   }
182   /***************************************************************************/
183   /* Start the CURL library. Note that this initialization is not threadsafe */
184   /* which imposes a constraint that the EVEL library is initialized before  */
185   /* any threads are started.                                                */
186   /***************************************************************************/
187   curl_rc = curl_global_init(CURL_GLOBAL_SSL);
188   if (curl_rc != CURLE_OK)
189   {
190     rc = EVEL_CURL_LIBRARY_FAIL;
191     log_error_state("Failed to initialize libCURL. Error code=%d", curl_rc);
192     goto exit_label;
193   }
194
195   /***************************************************************************/
196   /* Get a curl handle which we'll use for all of our output.                */
197   /***************************************************************************/
198   curl_handle = curl_easy_init();
199   if (curl_handle == NULL)
200   {
201     rc = EVEL_CURL_LIBRARY_FAIL;
202     log_error_state("Failed to get libCURL handle");
203     goto exit_label;
204   }
205
206   /***************************************************************************/
207   /* Prime the library to give friendly error codes.                         */
208   /***************************************************************************/
209   curl_rc = curl_easy_setopt(curl_handle,
210                              CURLOPT_ERRORBUFFER,
211                              curl_err_string);
212   if (curl_rc != CURLE_OK)
213   {
214     rc = EVEL_CURL_LIBRARY_FAIL;
215     log_error_state("Failed to initialize libCURL to provide friendly errors. "
216                     "Error code=%d", curl_rc);
217     goto exit_label;
218   }
219
220   /***************************************************************************/
221   /* If running in verbose mode generate more output.                        */
222   /***************************************************************************/
223   if (verbosity > 0)
224   {
225     curl_rc = curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
226     if (curl_rc != CURLE_OK)
227     {
228       rc = EVEL_CURL_LIBRARY_FAIL;
229       log_error_state("Failed to initialize libCURL to be verbose. "
230                       "Error code=%d", curl_rc);
231       goto exit_label;
232     }
233   }
234
235   /***************************************************************************/
236   /* Set the URL for the API.                                                */
237   /***************************************************************************/
238   curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, event_api_url);
239   if (curl_rc != CURLE_OK)
240   {
241     rc = EVEL_CURL_LIBRARY_FAIL;
242     log_error_state("Failed to initialize libCURL with the API URL. "
243                     "Error code=%d (%s)", curl_rc, curl_err_string);
244     goto exit_label;
245   }
246   EVEL_INFO("Initializing CURL to send events to: %s", event_api_url);
247
248   /***************************************************************************/
249   /* send all data to this function.                                         */
250   /***************************************************************************/
251   curl_rc = curl_easy_setopt(curl_handle,
252                              CURLOPT_WRITEFUNCTION,
253                              evel_write_callback);
254   if (curl_rc != CURLE_OK)
255   {
256     rc = EVEL_CURL_LIBRARY_FAIL;
257     log_error_state("Failed to initialize libCURL with the write callback. "
258                     "Error code=%d (%s)", curl_rc, curl_err_string);
259     goto exit_label;
260   }
261
262   /***************************************************************************/
263   /* configure local ip address if provided */
264   /* Default ip if NULL */
265   /***************************************************************************/
266   if( source_ip != NULL )
267   {
268     snprintf(local_address,sizeof(local_address),source_ip);
269     if( local_address[0] != '\0' )
270     {
271       curl_rc = curl_easy_setopt(curl_handle,
272                              CURLOPT_INTERFACE,
273                              local_address);
274       if (curl_rc != CURLE_OK)
275       {
276         rc = EVEL_CURL_LIBRARY_FAIL;
277         log_error_state("Failed to initialize libCURL with the local address. "
278                     "Error code=%d (%s)", curl_rc, curl_err_string);
279         goto exit_label;
280       }
281     }
282   }
283
284   /***************************************************************************/
285   /* configure SSL options for HTTPS transfers */
286   /***************************************************************************/
287   if( secure )
288   {
289     if( cert_file_path != NULL )
290     {
291       curl_rc = curl_easy_setopt(curl_handle,
292                              CURLOPT_SSLCERT,
293                              cert_file_path);
294       if (curl_rc != CURLE_OK)
295       {
296         rc = EVEL_CURL_LIBRARY_FAIL;
297         log_error_state("Failed to initialize libCURL with the client cert. "
298                     "Error code=%d (%s)", curl_rc, curl_err_string);
299         goto exit_label;
300       }
301     }
302
303     if( key_file_path != NULL )
304     {
305       curl_rc = curl_easy_setopt(curl_handle,
306                              CURLOPT_SSLKEY,
307                              key_file_path);
308       if (curl_rc != CURLE_OK)
309       {
310         rc = EVEL_CURL_LIBRARY_FAIL;
311         log_error_state("Failed to initialize libCURL with the client key. "
312                     "Error code=%d (%s)", curl_rc, curl_err_string);
313         goto exit_label;
314       }
315     }
316
317     if( ca_info != NULL )
318     {
319       curl_rc = curl_easy_setopt(curl_handle,
320                              CURLOPT_CAINFO,
321                              ca_info);
322       if (curl_rc != CURLE_OK)
323       {
324         rc = EVEL_CURL_LIBRARY_FAIL;
325         log_error_state("Failed to initialize libCURL with the CA cert file. "
326                     "Error code=%d (%s)", curl_rc, curl_err_string);
327         goto exit_label;
328       }
329     }
330
331     if( ca_file_path != NULL )
332     {
333       curl_rc = curl_easy_setopt(curl_handle,
334                              CURLOPT_CAPATH,
335                              ca_file_path);
336       if (curl_rc != CURLE_OK)
337       {
338         rc = EVEL_CURL_LIBRARY_FAIL;
339         log_error_state("Failed to initialize libCURL with the CA cert path. "
340                     "Error code=%d (%s)", curl_rc, curl_err_string);
341         goto exit_label;
342       }
343     }
344
345       curl_rc = curl_easy_setopt(curl_handle,
346                              CURLOPT_SSL_VERIFYPEER,
347                              verify_peer);
348       if (curl_rc != CURLE_OK)
349       {
350         rc = EVEL_CURL_LIBRARY_FAIL;
351         log_error_state("Failed to initialize libCURL with SSL Server verification. "
352                     "Error code=%d (%s)", curl_rc, curl_err_string);
353         goto exit_label;
354       }
355       curl_rc = curl_easy_setopt(curl_handle,
356                              CURLOPT_SSL_VERIFYHOST,
357                              verify_host);
358       if (curl_rc != CURLE_OK)
359       {
360         rc = EVEL_CURL_LIBRARY_FAIL;
361         log_error_state("Failed to initialize libCURL with Client host verification. "
362                     "Error code=%d (%s)", curl_rc, curl_err_string);
363         goto exit_label;
364       }
365
366   }
367
368
369
370   /***************************************************************************/
371   /* some servers don't like requests that are made without a user-agent     */
372   /* field, so we provide one.                                               */
373   /***************************************************************************/
374   curl_rc = curl_easy_setopt(curl_handle,
375                              CURLOPT_USERAGENT,
376                              "libcurl-agent/1.0");
377   if (curl_rc != CURLE_OK)
378   {
379     rc = EVEL_CURL_LIBRARY_FAIL;
380     log_error_state("Failed to initialize libCURL to upload. "
381                     "Error code=%d (%s)", curl_rc, curl_err_string);
382     goto exit_label;
383   }
384
385   /***************************************************************************/
386   /* Specify that we are going to POST data.                                 */
387   /***************************************************************************/
388   curl_rc = curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);
389   if (curl_rc != CURLE_OK)
390   {
391     rc = EVEL_CURL_LIBRARY_FAIL;
392     log_error_state("Failed to initialize libCURL to upload. "
393                     "Error code=%d (%s)", curl_rc, curl_err_string);
394     goto exit_label;
395   }
396
397   /***************************************************************************/
398   /* we want to use our own read function.                                   */
399   /***************************************************************************/
400   curl_rc = curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, read_callback);
401   if (curl_rc != CURLE_OK)
402   {
403     rc = EVEL_CURL_LIBRARY_FAIL;
404     log_error_state("Failed to initialize libCURL to upload using read "
405                     "function. Error code=%d (%s)", curl_rc, curl_err_string);
406     goto exit_label;
407   }
408
409   /***************************************************************************/
410   /* All of our events are JSON encoded.  We also suppress the               */
411   /* Expect: 100-continue   header that we would otherwise get since it      */
412   /* confuses some servers.                                                  */
413   /*                                                                         */
414   /* @TODO: do AT&T want this behavior?                                      */
415   /***************************************************************************/
416   hdr_chunk = curl_slist_append(hdr_chunk, "Content-type: application/json");
417   hdr_chunk = curl_slist_append(hdr_chunk, "Expect:");
418
419   /***************************************************************************/
420   /* set our custom set of headers.                                         */
421   /***************************************************************************/
422   curl_rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, hdr_chunk);
423   if (curl_rc != CURLE_OK)
424   {
425     rc = EVEL_CURL_LIBRARY_FAIL;
426     log_error_state("Failed to initialize libCURL to use custom headers. "
427                     "Error code=%d (%s)", curl_rc, curl_err_string);
428     goto exit_label;
429   }
430
431   /***************************************************************************/
432   /* Set the timeout for the operation.                                      */
433   /***************************************************************************/
434   curl_rc = curl_easy_setopt(curl_handle,
435                              CURLOPT_TIMEOUT,
436                              EVEL_API_TIMEOUT);
437   if (curl_rc != CURLE_OK)
438   {
439     rc = EVEL_CURL_LIBRARY_FAIL;
440     log_error_state("Failed to initialize libCURL for API timeout. "
441                     "Error code=%d (%s)", curl_rc, curl_err_string);
442     goto exit_label;
443   }
444
445   /***************************************************************************/
446   /* Set that we want Basic authentication with username:password Base-64    */
447   /* encoded for the operation.                                              */
448   /***************************************************************************/
449   curl_rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
450   if (curl_rc != CURLE_OK)
451   {
452     rc = EVEL_CURL_LIBRARY_FAIL;
453     log_error_state("Failed to initialize libCURL for Basic Authentication. "
454                     "Error code=%d (%s)", curl_rc, curl_err_string);
455     goto exit_label;
456   }
457   curl_rc = curl_easy_setopt(curl_handle, CURLOPT_USERNAME, username);
458   if (curl_rc != CURLE_OK)
459   {
460     rc = EVEL_CURL_LIBRARY_FAIL;
461     log_error_state("Failed to initialize libCURL with username. "
462                     "Error code=%d (%s)", curl_rc, curl_err_string);
463     goto exit_label;
464   }
465   curl_rc = curl_easy_setopt(curl_handle, CURLOPT_PASSWORD, password);
466   if (curl_rc != CURLE_OK)
467   {
468     rc = EVEL_CURL_LIBRARY_FAIL;
469     log_error_state("Failed to initialize libCURL with password. "
470                     "Error code=%d (%s)", curl_rc, curl_err_string);
471     goto exit_label;
472   }
473
474   /***************************************************************************/
475   /* Initialize a message ring-buffer to be used between the foreground and  */
476   /* the thread which sends the messages.  This can't fail.                  */
477   /***************************************************************************/
478   if( ring_buf_size < EVEL_EVENT_BUFFER_DEPTH )
479   {
480     log_error_state("Warning: Failed to initialize Ring buffer size to %d. "
481                     ring_buf_size);
482     goto exit_label;
483   }
484   ring_buffer_initialize(&event_buffer, EVEL_EVENT_BUFFER_DEPTH);
485
486   /***************************************************************************/
487   /* Initialize the priority post buffer to empty.                           */
488   /***************************************************************************/
489   priority_post.memory = NULL;
490
491 exit_label:
492   EVEL_EXIT();
493
494   return(rc);
495 }
496
497 /**************************************************************************//**
498  * Run the event handler.
499  *
500  * Spawns the thread responsible for handling events and sending them to the
501  * API.
502  *
503  *  @return Status code.
504  *  @retval ::EVEL_SUCCESS if everything OK.
505  *  @retval One of ::EVEL_ERR_CODES if there was a problem.
506  *****************************************************************************/
507 EVEL_ERR_CODES event_handler_run()
508 {
509   EVEL_ERR_CODES rc = EVEL_SUCCESS;
510   int pthread_rc = 0;
511
512   EVEL_ENTER();
513
514   /***************************************************************************/
515   /* Start the event handler thread.                                         */
516   /***************************************************************************/
517   evt_handler_state = EVT_HANDLER_INACTIVE;
518   pthread_rc = pthread_create(&evt_handler_thread, NULL, event_handler, NULL);
519   if (pthread_rc != 0)
520   {
521     rc = EVEL_PTHREAD_LIBRARY_FAIL;
522     log_error_state("Failed to start event handler thread. "
523                     "Error code=%d", pthread_rc);
524   }
525
526   EVEL_EXIT()
527   return rc;
528 }
529
530 /**************************************************************************//**
531  * Terminate the event handler.
532  *
533  * Shuts down the event handler thread in as clean a way as possible. Sets the
534  * global exit flag and then signals the thread to interrupt it since it's
535  * most likely waiting on the ring-buffer.
536  *
537  * Having achieved an orderly shutdown of the event handler thread, clean up
538  * the cURL library's resources cleanly.
539  *
540  *  @return Status code.
541  *  @retval ::EVEL_SUCCESS if everything OK.
542  *  @retval One of ::EVEL_ERR_CODES if there was a problem.
543  *****************************************************************************/
544 EVEL_ERR_CODES event_handler_terminate()
545 {
546   EVEL_ERR_CODES rc = EVEL_SUCCESS;
547
548   EVEL_ENTER();
549   EVENT_INTERNAL *event = NULL;
550
551   /***************************************************************************/
552   /* Make sure that we were initialized before trying to terminate the       */
553   /* event handler thread.                                                   */
554   /***************************************************************************/
555   if (evt_handler_state != EVT_HANDLER_UNINITIALIZED)
556   {
557     /*************************************************************************/
558     /* Make sure that the event handler knows it's time to die.              */
559     /*************************************************************************/
560     event = evel_new_internal_event(EVT_CMD_TERMINATE,"EVELinternal","EVELid");
561     if (event == NULL)
562     {
563       /***********************************************************************/
564       /* We failed to get an event, but we don't bail out - we will just     */
565       /* clean up what we can and continue on our way, since we're exiting   */
566       /* anyway.                                                             */
567       /***********************************************************************/
568       EVEL_ERROR("Failed to get internal event - perform dirty exit instead!");
569     }
570     else
571     {
572       /***********************************************************************/
573       /* Post the event then wait for the Event Handler to exit.  Set the    */
574       /* global command, too, in case the ring-buffer is full.               */
575       /***********************************************************************/
576       EVEL_DEBUG("Sending event to Event Hander to request it to exit.");
577       evt_handler_state = EVT_HANDLER_REQUEST_TERMINATE;
578       evel_post_event((EVENT_HEADER *) event);
579       pthread_join(evt_handler_thread, NULL);
580       EVEL_DEBUG("Event Handler thread has exited.");
581     }
582   }
583   else
584   {
585     EVEL_DEBUG("Event handler was not initialized, so no need to kill it");
586   }
587
588   /***************************************************************************/
589   /* Clean-up the cURL library.                                              */
590   /***************************************************************************/
591   if (curl_handle != NULL)
592   {
593     curl_easy_cleanup(curl_handle);
594     curl_handle = NULL;
595   }
596   if (hdr_chunk != NULL)
597   {
598     curl_slist_free_all(hdr_chunk);
599     hdr_chunk = NULL;
600   }
601
602   /***************************************************************************/
603   /* Free off the stored API URL strings.                                    */
604   /***************************************************************************/
605   if (evel_event_api_url != NULL)
606   {
607     free(evel_event_api_url);
608     evel_event_api_url = NULL;
609   }
610   if (evel_batch_api_url != NULL)
611   {
612     free(evel_batch_api_url);
613     evel_batch_api_url = NULL;
614   }
615   if (evel_throt_api_url != NULL)
616   {
617     free(evel_throt_api_url);
618     evel_throt_api_url = NULL;
619   }
620
621   EVEL_EXIT();
622   return rc;
623 }
624
625 /**************************************************************************//**
626  * Post an event.
627  *
628  * @note  So far as the caller is concerned, successfully posting the event
629  * relinquishes all responsibility for the event - the library will take care
630  * of freeing the event in due course.
631
632  * @param event   The event to be posted.
633  *
634  * @returns Status code
635  * @retval  EVEL_SUCCESS On success
636  * @retval  "One of ::EVEL_ERR_CODES" On failure.
637  *****************************************************************************/
638 EVEL_ERR_CODES evel_post_event(EVENT_HEADER * event)
639 {
640   int rc = EVEL_SUCCESS;
641
642   EVEL_ENTER();
643
644   /***************************************************************************/
645   /* Check preconditions.                                                    */
646   /***************************************************************************/
647   assert(event != NULL);
648
649   /***************************************************************************/
650   /* We need to make sure that we are either initializing or running         */
651   /* normally before writing the event into the buffer so that we can        */
652   /* guarantee that the ring-buffer empties  properly on exit.               */
653   /***************************************************************************/
654   if ((evt_handler_state == EVT_HANDLER_ACTIVE) ||
655       (evt_handler_state == EVT_HANDLER_INACTIVE) ||
656       (evt_handler_state == EVT_HANDLER_REQUEST_TERMINATE))
657   {
658     if (ring_buffer_write(&event_buffer, event) == 0)
659     {
660       log_error_state("Failed to write event to buffer - event dropped!");
661       rc = EVEL_EVENT_BUFFER_FULL;
662       evel_free_event(event);
663     }
664   }
665   else
666   {
667     /*************************************************************************/
668     /* System is not in active operation, so reject the event.               */
669     /*************************************************************************/
670     log_error_state("Event Handler system not active - event dropped!");
671     rc = EVEL_EVENT_HANDLER_INACTIVE;
672     evel_free_event(event);
673   }
674
675   EVEL_EXIT();
676   return (rc);
677 }
678
679 /**************************************************************************//**
680  * Post an event to the Vendor Event Listener API.
681  *
682  * @returns Status code
683  * @retval  EVEL_SUCCESS On success
684  * @retval  "One of ::EVEL_ERR_CODES" On failure.
685  *****************************************************************************/
686 static EVEL_ERR_CODES evel_post_api(char * msg, size_t size)
687 {
688   int rc = EVEL_SUCCESS;
689   CURLcode curl_rc = CURLE_OK;
690   MEMORY_CHUNK rx_chunk;
691   MEMORY_CHUNK tx_chunk;
692   int http_response_code = 0;
693
694   EVEL_ENTER();
695
696   /***************************************************************************/
697   /* Create the memory chunk to be used for the response to the post.  The   */
698   /* will be realloced.                                                      */
699   /***************************************************************************/
700   rx_chunk.memory = malloc(1);
701   assert(rx_chunk.memory != NULL);
702   rx_chunk.size = 0;
703
704   /***************************************************************************/
705   /* Create the memory chunk to be sent as the body of the post.             */
706   /***************************************************************************/
707   tx_chunk.memory = msg;
708   tx_chunk.size = size;
709   EVEL_DEBUG("Sending chunk of size %d", tx_chunk.size);
710
711   /***************************************************************************/
712   /* Point to the data to be received.                                       */
713   /***************************************************************************/
714   curl_rc = curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &rx_chunk);
715   if (curl_rc != CURLE_OK)
716   {
717     rc = EVEL_CURL_LIBRARY_FAIL;
718     log_error_state("Failed to initialize libCURL to upload. "
719                     "Error code=%d (%s)", curl_rc, curl_err_string);
720     goto exit_label;
721   }
722   EVEL_DEBUG("Initialized data to receive");
723
724   /***************************************************************************/
725   /* Pointer to pass to our read function                                    */
726   /***************************************************************************/
727   curl_rc = curl_easy_setopt(curl_handle, CURLOPT_READDATA, &tx_chunk);
728   if (curl_rc != CURLE_OK)
729   {
730     rc = EVEL_CURL_LIBRARY_FAIL;
731     log_error_state("Failed to set upload data for libCURL to upload. "
732                     "Error code=%d (%s)", curl_rc, curl_err_string);
733     goto exit_label;
734   }
735   EVEL_DEBUG("Initialized data to send");
736
737   /***************************************************************************/
738   /* Size of the data to transmit.                                           */
739   /***************************************************************************/
740   curl_rc = curl_easy_setopt(curl_handle,
741                              CURLOPT_POSTFIELDSIZE,
742                              tx_chunk.size);
743   if (curl_rc != CURLE_OK)
744   {
745     rc = EVEL_CURL_LIBRARY_FAIL;
746     log_error_state("Failed to set length of upload data for libCURL to "
747                     "upload.  Error code=%d (%s)", curl_rc, curl_err_string);
748     goto exit_label;
749   }
750   EVEL_DEBUG("Initialized length of data to send");
751
752   /***************************************************************************/
753   /* Now run off and do what you've been told!                               */
754   /***************************************************************************/
755   curl_rc = curl_easy_perform(curl_handle);
756   if (curl_rc != CURLE_OK)
757   {
758     rc = EVEL_CURL_LIBRARY_FAIL;
759     log_error_state("Failed to transfer an event to Vendor Event Listener! "
760                     "Error code=%d (%s)", curl_rc, curl_err_string);
761     EVEL_ERROR("Dropped event: %s", msg);
762     goto exit_label;
763   }
764
765   /***************************************************************************/
766   /* See what response we got - any 2XX response is good.                    */
767   /***************************************************************************/
768   curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_response_code);
769   EVEL_DEBUG("HTTP response code: %d", http_response_code);
770   if ((http_response_code / 100) == 2)
771   {
772     /*************************************************************************/
773     /* If the server responded with data it may be interesting but not a     */
774     /* problem.                                                              */
775     /*************************************************************************/
776     if ((rx_chunk.size > 0) && (rx_chunk.memory != NULL))
777     {
778       EVEL_DEBUG("Server returned data = %d (%s)",
779                  rx_chunk.size,
780                  rx_chunk.memory);
781
782       /***********************************************************************/
783       /* If this is a response to priority post, then we're not interested.  */
784       /***********************************************************************/
785       if (priority_post.memory != NULL)
786       {
787         EVEL_ERROR("Ignoring priority post response");
788       }
789       else
790       {
791         evel_handle_event_response(&rx_chunk, &priority_post);
792       }
793     }
794   }
795   else
796   {
797     EVEL_ERROR("Unexpected HTTP response code: %d with data size %d (%s)",
798                 http_response_code,
799                 rx_chunk.size,
800                 rx_chunk.size > 0 ? rx_chunk.memory : "NONE");
801     EVEL_ERROR("Potentially dropped event: %s", msg);
802   }
803
804 exit_label:
805   free(rx_chunk.memory);
806   EVEL_EXIT();
807   return(rc);
808 }
809
810 /**************************************************************************//**
811  * Callback function to provide data to send.
812  *
813  * Copy data into the supplied buffer, read_callback::ptr, checking size
814  * limits.
815  *
816  * @returns   Number of bytes placed into read_callback::ptr. 0 for EOF.
817  *****************************************************************************/
818 static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
819 {
820   size_t rtn = 0;
821   size_t bytes_to_write = 0;
822   MEMORY_CHUNK *tx_chunk = (MEMORY_CHUNK *)userp;
823
824   EVEL_ENTER();
825
826   bytes_to_write = min(size*nmemb, tx_chunk->size);
827
828   if (bytes_to_write > 0)
829   {
830     EVEL_DEBUG("Going to try to write %d bytes", bytes_to_write);
831     strncpy((char *)ptr, tx_chunk->memory, bytes_to_write);
832     tx_chunk->memory += bytes_to_write;
833     tx_chunk->size -= bytes_to_write;
834     rtn = bytes_to_write;
835   }
836   else
837   {
838     EVEL_DEBUG("Reached EOF");
839   }
840
841   EVEL_EXIT();
842   return rtn;
843 }
844
845 /**************************************************************************//**
846  * Callback function to provide returned data.
847  *
848  * Copy data into the supplied buffer, write_callback::ptr, checking size
849  * limits.
850  *
851  * @returns   Number of bytes placed into write_callback::ptr. 0 for EOF.
852  *****************************************************************************/
853 size_t evel_write_callback(void *contents,
854                              size_t size,
855                              size_t nmemb,
856                              void *userp)
857 {
858   size_t realsize = size * nmemb;
859   MEMORY_CHUNK * rx_chunk = (MEMORY_CHUNK *)userp;
860
861   EVEL_ENTER();
862
863   EVEL_DEBUG("Called with %d chunks of %d size = %d", nmemb, size, realsize);
864   EVEL_DEBUG("rx chunk size is %d", rx_chunk->size);
865
866   rx_chunk->memory = realloc(rx_chunk->memory, rx_chunk->size + realsize + 1);
867   if(rx_chunk->memory == NULL) {
868     /* out of memory! */
869     printf("not enough memory (realloc returned NULL)\n");
870     return 0;
871   }
872
873   memcpy(&(rx_chunk->memory[rx_chunk->size]), contents, realsize);
874   rx_chunk->size += realsize;
875   rx_chunk->memory[rx_chunk->size] = 0;
876
877   EVEL_DEBUG("Rx data: %s", rx_chunk->memory);
878   EVEL_DEBUG("Returning: %d", realsize);
879
880   EVEL_EXIT();
881   return realsize;
882 }
883
884 /**************************************************************************//**
885  * Event Handler.
886  *
887  * Watch for messages coming on the internal queue and send them to the
888  * listener.
889  *
890  * param[in]  arg  Argument - unused.
891  *****************************************************************************/
892 static void * event_handler(void * arg __attribute__ ((unused)))
893 {
894   int old_type = 0;
895   EVENT_HEADER * msg = NULL;
896   EVENT_INTERNAL * internal_msg = NULL;
897   int json_size = 0;
898   char json_body[EVEL_MAX_JSON_BODY];
899   int rc = EVEL_SUCCESS;
900   CURLcode curl_rc;
901
902   EVEL_INFO("Event handler thread started");
903
904   /***************************************************************************/
905   /* Set this thread to be cancellable immediately.                          */
906   /***************************************************************************/
907   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type);
908
909   /***************************************************************************/
910   /* Set the handler as active, defending against weird situations like      */
911   /* immediately shutting down after initializing the library so the         */
912   /* handler never gets started up properly.                                 */
913   /***************************************************************************/
914   if (evt_handler_state == EVT_HANDLER_INACTIVE)
915   {
916     evt_handler_state = EVT_HANDLER_ACTIVE;
917   }
918   else
919   {
920     EVEL_ERROR("Event Handler State was not INACTIVE at start-up - "
921                "Handler will exit immediately!");
922   }
923
924   while (evt_handler_state == EVT_HANDLER_ACTIVE)
925   {
926     /*************************************************************************/
927     /* Wait for a message to be received.                                    */
928     /*************************************************************************/
929     EVEL_DEBUG("Event handler getting any messages");
930     msg = ring_buffer_read(&event_buffer);
931
932     /*************************************************************************/
933     /* Internal events get special treatment while regular events get posted */
934     /* to the far side.                                                      */
935     /*************************************************************************/
936     if (msg->event_domain == EVEL_DOMAIN_BATCH )
937     {
938       EVEL_DEBUG("Batch event received");
939
940       /***********************************************************************/
941       /* Encode the event in JSON.                                           */
942       /***********************************************************************/
943       json_size = evel_json_encode_batch_event(json_body, EVEL_MAX_JSON_BODY, msg);
944
945       /***************************************************************************/
946       /* Set the URL for the API.                                                */
947       /***************************************************************************/
948       curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_batch_api_url);
949       if (curl_rc != CURLE_OK)
950       {
951         rc = EVEL_CURL_LIBRARY_FAIL;
952         log_error_state("Failed to initialize libCURL with the Batch API URL. "
953                     "Error code=%d (%s)", curl_rc, curl_err_string);
954       }
955
956       /***********************************************************************/
957       /* Send the JSON across the API.                                       */
958       /***********************************************************************/
959       EVEL_DEBUG("Sending Batch JSON of size %d is: %s", json_size, json_body);
960       rc = evel_post_api(json_body, json_size);
961       if (rc != EVEL_SUCCESS)
962       {
963         EVEL_ERROR("Failed to transfer the data. Error code=%d", rc);
964       }
965     }
966     else if (msg->event_domain != EVEL_DOMAIN_INTERNAL )
967     {
968       EVEL_DEBUG("External event received");
969
970       /***********************************************************************/
971       /* Encode the event in JSON.                                           */
972       /***********************************************************************/
973       json_size = evel_json_encode_event(json_body, EVEL_MAX_JSON_BODY, msg);
974
975       /***************************************************************************/
976       /* Set the URL for the API.                                                */
977       /***************************************************************************/
978       curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_event_api_url);
979       if (curl_rc != CURLE_OK)
980       {
981         rc = EVEL_CURL_LIBRARY_FAIL;
982         log_error_state("Failed to initialize libCURL with the API URL. "
983                     "Error code=%d (%s)", curl_rc, curl_err_string);
984       }
985
986       /***********************************************************************/
987       /* Send the JSON across the API.                                       */
988       /***********************************************************************/
989       EVEL_DEBUG("Sending JSON of size %d is: %s", json_size, json_body);
990       rc = evel_post_api(json_body, json_size);
991       if (rc != EVEL_SUCCESS)
992       {
993         EVEL_ERROR("Failed to transfer the data. Error code=%d", rc);
994       }
995     }
996     else
997     {
998       EVEL_DEBUG("Internal event received");
999       internal_msg = (EVENT_INTERNAL *) msg;
1000       assert(internal_msg->command == EVT_CMD_TERMINATE);
1001       evt_handler_state = EVT_HANDLER_TERMINATING;
1002     }
1003
1004     /*************************************************************************/
1005     /* We are responsible for freeing the memory.                            */
1006     /*************************************************************************/
1007     evel_free_event(msg);
1008     msg = NULL;
1009
1010     /*************************************************************************/
1011     /* There may be a single priority post to be sent.                       */
1012     /*************************************************************************/
1013     if (priority_post.memory != NULL)
1014     {
1015       EVEL_DEBUG("Priority Post");
1016
1017       /***********************************************************************/
1018       /* Set the URL for the throttling API.                                 */
1019       /***********************************************************************/
1020       curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_throt_api_url);
1021       if (curl_rc != CURLE_OK)
1022       {
1023         /*********************************************************************/
1024         /* This is only likely to happen with CURLE_OUT_OF_MEMORY, in which  */
1025         /* case we carry on regardless.                                      */
1026         /*********************************************************************/
1027         EVEL_ERROR("Failed to set throttling URL. Error code=%d", rc);
1028       }
1029       else
1030       {
1031         rc = evel_post_api(priority_post.memory, priority_post.size);
1032         if (rc != EVEL_SUCCESS)
1033         {
1034           EVEL_ERROR("Failed to transfer priority post. Error code=%d", rc);
1035         }
1036       }
1037
1038       /***********************************************************************/
1039       /* Reinstate the URL for the event API.                                */
1040       /***********************************************************************/
1041       curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_event_api_url);
1042       if (curl_rc != CURLE_OK)
1043       {
1044         /*********************************************************************/
1045         /* This is only likely to happen with CURLE_OUT_OF_MEMORY, in which  */
1046         /* case we carry on regardless.                                      */
1047         /*********************************************************************/
1048         EVEL_ERROR("Failed to reinstate events URL. Error code=%d", rc);
1049       }
1050
1051       /***********************************************************************/
1052       /* We are responsible for freeing the memory.                          */
1053       /***********************************************************************/
1054       free(priority_post.memory);
1055       priority_post.memory = NULL;
1056     }
1057   }
1058
1059   /***************************************************************************/
1060   /* The event handler is now exiting. The ring-buffer could contain events  */
1061   /* which have not been processed, so deplete those.  Because we've been    */
1062   /* asked to exit we can be confident that the foreground will have stopped */
1063   /* sending events in so we know that this process will conclude!           */
1064   /***************************************************************************/
1065   evt_handler_state = EVT_HANDLER_TERMINATING;
1066   while (!ring_buffer_is_empty(&event_buffer))
1067   {
1068     EVEL_DEBUG("Reading event from buffer");
1069     msg = ring_buffer_read(&event_buffer);
1070     evel_free_event(msg);
1071   }
1072   evt_handler_state = EVT_HANDLER_TERMINATED;
1073   EVEL_INFO("Event handler thread stopped");
1074
1075   return (NULL);
1076 }
1077
1078 /**************************************************************************//**
1079  * Handle a JSON response from the listener, contained in a ::MEMORY_CHUNK.
1080  *
1081  * Tokenize the response, and decode any tokens found.
1082  *
1083  * @param chunk         The memory chunk containing the response.
1084  * @param post          The memory chunk in which to place any resulting POST.
1085  *****************************************************************************/
1086 void evel_handle_event_response(const MEMORY_CHUNK * const chunk,
1087                                 MEMORY_CHUNK * const post)
1088 {
1089   jsmn_parser json_parser;
1090   jsmntok_t json_tokens[EVEL_MAX_RESPONSE_TOKENS];
1091   int num_tokens = 0;
1092
1093   EVEL_ENTER();
1094
1095   /***************************************************************************/
1096   /* Check preconditions.                                                    */
1097   /***************************************************************************/
1098   assert(chunk != NULL);
1099   assert(priority_post.memory == NULL);
1100
1101   EVEL_DEBUG("Response size = %d", chunk->size);
1102   EVEL_DEBUG("Response = %s", chunk->memory);
1103
1104   /***************************************************************************/
1105   /* Initialize the parser and tokenize the response.                        */
1106   /***************************************************************************/
1107   jsmn_init(&json_parser);
1108   num_tokens = jsmn_parse(&json_parser,
1109                           chunk->memory,
1110                           chunk->size,
1111                           json_tokens,
1112                           EVEL_MAX_RESPONSE_TOKENS);
1113
1114   if (num_tokens < 0)
1115   {
1116     EVEL_ERROR("Failed to parse JSON response.  "
1117                "Error code=%d", num_tokens);
1118   }
1119   else if (num_tokens == 0)
1120   {
1121     EVEL_DEBUG("No tokens found in JSON response");
1122   }
1123   else
1124   {
1125     EVEL_DEBUG("Decode JSON response tokens");
1126     if (!evel_handle_response_tokens(chunk, json_tokens, num_tokens, post))
1127     {
1128       EVEL_ERROR("Failed to handle JSON response.");
1129     }
1130   }
1131
1132   EVEL_EXIT();
1133 }
1134
1135 /**************************************************************************//**
1136  * Handle a JSON response from the listener, as a list of tokens from JSMN.
1137  *
1138  * @param chunk         Memory chunk containing the JSON buffer.
1139  * @param json_tokens   Array of tokens to handle.
1140  * @param num_tokens    The number of tokens to handle.
1141  * @param post          The memory chunk in which to place any resulting POST.
1142  * @return true if we handled the response, false otherwise.
1143  *****************************************************************************/
1144 bool evel_handle_response_tokens(const MEMORY_CHUNK * const chunk,
1145                                  const jsmntok_t * const json_tokens,
1146                                  const int num_tokens,
1147                                  MEMORY_CHUNK * const post)
1148 {
1149   bool json_ok = false;
1150
1151   EVEL_ENTER();
1152
1153   /***************************************************************************/
1154   /* Check preconditions.                                                    */
1155   /***************************************************************************/
1156   assert(chunk != NULL);
1157   assert(json_tokens != NULL);
1158   assert(num_tokens < EVEL_MAX_RESPONSE_TOKENS);
1159
1160   /***************************************************************************/
1161   /* Peek at the tokens to decide what the response it, then call the        */
1162   /* appropriate handler to handle it.  There is only one handler at this    */
1163   /* point.                                                                  */
1164   /***************************************************************************/
1165   if (evel_tokens_match_command_list(chunk, json_tokens, num_tokens))
1166   {
1167     json_ok = evel_handle_command_list(chunk, json_tokens, num_tokens, post);
1168   }
1169
1170   EVEL_EXIT();
1171
1172   return json_ok;
1173 }
1174
1175 /**************************************************************************//**
1176  * Determine whether a list of tokens looks like a "commandList" response.
1177  *
1178  * @param chunk         Memory chunk containing the JSON buffer.
1179  * @param json_tokens   Token to check.
1180  * @param num_tokens    The number of tokens to handle.
1181  * @return true if the tokens look like a "commandList" match, or false.
1182  *****************************************************************************/
1183 bool evel_tokens_match_command_list(const MEMORY_CHUNK * const chunk,
1184                                     const jsmntok_t * const json_tokens,
1185                                     const int num_tokens)
1186 {
1187   bool result = false;
1188
1189   EVEL_ENTER();
1190
1191   /***************************************************************************/
1192   /* Make some checks on the basic layout of the commandList.                */
1193   /***************************************************************************/
1194   if ((num_tokens > 3) &&
1195       (json_tokens[0].type == JSMN_OBJECT) &&
1196       (json_tokens[1].type == JSMN_STRING) &&
1197       (json_tokens[2].type == JSMN_ARRAY) &&
1198       (evel_token_equals_string(chunk, &json_tokens[1], "commandList")))
1199   {
1200     result = true;
1201   }
1202
1203   EVEL_EXIT();
1204
1205   return result;
1206 }
1207
1208 /**************************************************************************//**
1209  * Check that a string token matches a given input string.
1210  *
1211  * @param chunk         Memory chunk containing the JSON buffer.
1212  * @param json_token    Token to check.
1213  * @param check_string  String to check it against.
1214  * @return true if the strings match, or false.
1215  *****************************************************************************/
1216 bool evel_token_equals_string(const MEMORY_CHUNK * const chunk,
1217                               const jsmntok_t * json_token,
1218                               const char * check_string)
1219 {
1220   bool result = false;
1221
1222   EVEL_ENTER();
1223
1224   const int token_length = json_token->end - json_token->start;
1225   const char * const token_string = chunk->memory + json_token->start;
1226
1227   if (token_length == (int)strlen(check_string))
1228   {
1229     result = (strncmp(token_string, check_string, token_length) == 0);
1230   }
1231
1232   EVEL_EXIT();
1233
1234   return result;
1235 }