AT&T ECOMP Vendor Event Listener library  0.1
evel_event_mgr.c
Go to the documentation of this file.
1 /**************************************************************************/
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 /**************************************************************************/
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 /**************************************************************************/
77 static char curl_err_string[CURL_ERROR_SIZE] = "<NULL>";
78 
79 /**************************************************************************/
82 static CURL * curl_handle = NULL;
83 
84 /**************************************************************************/
87 static struct curl_slist * hdr_chunk = NULL;
88 
89 /**************************************************************************/
92 static ring_buffer event_buffer;
93 
94 /**************************************************************************/
98 static MEMORY_CHUNK priority_post;
99 
100 /**************************************************************************/
104 static pthread_t evt_handler_thread;
105 
106 /**************************************************************************/
110 static EVT_HANDLER_STATE evt_handler_state = EVT_HANDLER_UNINITIALIZED;
111 
112 /**************************************************************************/
115 static char * evel_event_api_url;
116 static char * evel_throt_api_url;
117 
118 /**************************************************************************/
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  {
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  {
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  {
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  {
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  {
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,
232  if (curl_rc != CURLE_OK)
233  {
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  {
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  {
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  {
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  {
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  {
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  {
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  {
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  {
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 /**************************************************************************/
372 {
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  {
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 /**************************************************************************/
409 {
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  /*************************************************************************/
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 /**************************************************************************/
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!");
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!");
531  evel_free_event(event);
532  }
533 
534  EVEL_EXIT();
535  return (rc);
536 }
537 
538 /**************************************************************************/
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  {
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  {
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  {
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  {
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 /**************************************************************************/
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 /**************************************************************************/
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 /**************************************************************************/
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  /*************************************************************************/
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 /**************************************************************************/
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,
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 /**************************************************************************/
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 /**************************************************************************/
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 /**************************************************************************/
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 }
JSON token description.
Definition: jsmn.h:40
#define EVEL_DEBUG(FMT,...)
Definition: evel.h:3621
void * ring_buffer_read(ring_buffer *buffer)
Read an element from a ring_buffer.
Definition: ring_buffer.c:100
#define EVEL_INFO(FMT,...)
Definition: evel.h:3622
void evel_free_event(void *event)
Free an event.
Definition: evel.c:299
EVT_HANDLER_COMMAND command
A chunk of memory used in the cURL functions.
Definition: evel_internal.h:77
Internal event.
Non-specific failure.
Definition: evel.h:71
The event handler thread not started.
Definition: evel_internal.h:96
A memory allocation failure occurred.
Definition: evel.h:74
#define EVEL_EXIT()
Definition: evel.h:3631
The event handler thread is started.
Definition: evel_internal.h:97
Initial stages of shutdown.
Definition: evel_internal.h:98
#define EVEL_ENTER()
Definition: evel.h:3626
int ring_buffer_write(ring_buffer *buffer, void *msg)
Write an element into a ring_buffer.
Definition: ring_buffer.c:146
EVEL_ERR_CODES evel_post_event(EVENT_HEADER *event)
Post an event.
Header for EVEL library.
int evel_json_encode_event(char *json, int max_size, EVENT_HEADER *event)
Encode the event as a JSON event object according to AT&T&#39;s schema.
Definition: evel_event.c:381
#define min(a, b)
Definition: evel_internal.h:53
A ring buffer.
EVENT_INTERNAL * evel_new_internal_event(EVT_HANDLER_COMMAND command)
Create a new internal event.
JSON parser.
Definition: jsmn.h:54
void ring_buffer_initialize(ring_buffer *buffer, int size)
Ring buffer initialization.
Definition: ring_buffer.c:53
Too many events in the ring-buffer.
Definition: evel.h:75
A cURL library operation failed.
Definition: evel.h:72
#define EVEL_ERROR(FMT,...)
Definition: evel.h:3624
int start
Definition: jsmn.h:42
int ring_buffer_is_empty(ring_buffer *buffer)
Tests whether there is data in the ring_buffer.
Definition: ring_buffer.c:194
EVEL_ERR_CODES
Error codes.
Definition: evel.h:68
EVEL_ERR_CODES event_handler_initialize(const char *const event_api_url, const char *const throt_api_url, const char *const username, const char *const password, int verbosity)
Initialize the event handler.
EVEL_ERR_CODES event_handler_terminate()
Terminate the event handler.
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens)
Parse JSON string and fill tokens.
Definition: jsmn.c:151
EVEL_EVENT_DOMAINS event_domain
Definition: evel.h:420
The library cannot handle events.
Definition: evel_internal.h:95
int end
Definition: jsmn.h:43
void log_error_state(char *format,...)
Definition: evel_logging.c:98
void jsmn_init(jsmn_parser *parser)
Creates a new parser based over a given buffer with an array of tokens available. ...
Definition: jsmn.c:306
EVT_HANDLER_STATE
State of the Event Handler thread.
Definition: evel_internal.h:93
Event header.
Definition: evel.h:410
bool evel_handle_command_list(const MEMORY_CHUNK *const chunk, const jsmntok_t *const json_tokens, const int num_tokens, MEMORY_CHUNK *const post)
Handle a JSON response from the listener, as a list of tokens from JSMN.
size_t evel_write_callback(void *contents, size_t size, size_t nmemb, void *userp)
Callback function to provide returned data.
Ring buffer structure.
Definition: ring_buffer.h:45
EVEL_ERR_CODES event_handler_run()
Run the event handler.
EVEL throttle definitions.
EVEL internal definitions.
The ring-buffer is being depleted.
Definition: evel_internal.h:99
#define EVEL_MAX_JSON_BODY
Definition: evel.h:101
#define EVEL_MAX_RESPONSE_TOKENS
Maximum number of tokens that we allow for in a JSON response.
Definition: evel_throttle.h:55
void evel_handle_event_response(const MEMORY_CHUNK *const chunk, MEMORY_CHUNK *const post)
Handle a JSON response from the listener, contained in a MEMORY_CHUNK.