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