1 /*************************************************************************//**
3 * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 ****************************************************************************/
18 /**************************************************************************//**
22 * Simple event manager that is responsible for taking events (Heartbeats,
23 * Faults and Measurements) from the ring-buffer and posting them to the API.
25 ****************************************************************************/
32 #include <curl/curl.h>
35 #include "evel_internal.h"
36 #include "ring_buffer.h"
37 #include "evel_throttle.h"
39 /**************************************************************************//**
40 * How long we're prepared to wait for the API service to respond in
42 *****************************************************************************/
43 static const int EVEL_API_TIMEOUT = 5;
45 /*****************************************************************************/
46 /* Prototypes of locally scoped functions. */
47 /*****************************************************************************/
48 static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp);
49 static void * event_handler(void *arg);
50 static bool evel_handle_response_tokens(const MEMORY_CHUNK * const chunk,
51 const jsmntok_t * const json_tokens,
53 MEMORY_CHUNK * const post);
54 static bool evel_tokens_match_command_list(const MEMORY_CHUNK * const chunk,
55 const jsmntok_t * const json_token,
56 const int num_tokens);
57 static bool evel_token_equals_string(const MEMORY_CHUNK * const chunk,
58 const jsmntok_t * const json_token,
59 const char * check_string);
61 /**************************************************************************//**
62 * Buffers for error strings from libcurl.
63 *****************************************************************************/
64 static char curl_err_string[CURL_ERROR_SIZE] = "<NULL>";
66 /**************************************************************************//**
67 * Handle for the API into libcurl.
68 *****************************************************************************/
69 static CURL * curl_handle = NULL;
71 /**************************************************************************//**
72 * Special headers that we send.
73 *****************************************************************************/
74 static struct curl_slist * hdr_chunk = NULL;
76 /**************************************************************************//**
77 * Message queue for sending events to the API.
78 *****************************************************************************/
79 static ring_buffer event_buffer;
81 /**************************************************************************//**
82 * Single pending priority post, which can be generated as a result of a
83 * response to an event. Currently only used to respond to a commandList.
84 *****************************************************************************/
85 static MEMORY_CHUNK priority_post;
87 /**************************************************************************//**
88 * The thread which is responsible for handling events off of the ring-buffer
89 * and posting them to the Event Handler API.
90 *****************************************************************************/
91 static pthread_t evt_handler_thread;
93 /**************************************************************************//**
94 * Variable to convey to the event handler thread what the foreground wants it
96 *****************************************************************************/
97 static EVT_HANDLER_STATE evt_handler_state = EVT_HANDLER_UNINITIALIZED;
99 /**************************************************************************//**
100 * The configured API URL for event and throttling.
101 *****************************************************************************/
102 static char * evel_event_api_url;
103 static char * evel_throt_api_url;
105 /**************************************************************************//**
106 * Initialize the event handler.
108 * Primarily responsible for getting CURL ready for use.
110 * @param[in] event_api_url
111 * The URL where the Vendor Event Listener API is expected
113 * @param[in] throt_api_url
114 * The URL where the Throttling API is expected to be.
115 * @param[in] username The username for the Basic Authentication of requests.
116 * @param[in] password The password for the Basic Authentication of requests.
117 * @param verbosity 0 for normal operation, positive values for chattier
119 *****************************************************************************/
120 EVEL_ERR_CODES event_handler_initialize(const char * const event_api_url,
121 const char * const throt_api_url,
122 const char * const username,
123 const char * const password,
126 int rc = EVEL_SUCCESS;
127 CURLcode curl_rc = CURLE_OK;
131 /***************************************************************************/
132 /* Check assumptions. */
133 /***************************************************************************/
134 assert(event_api_url != NULL);
135 assert(throt_api_url != NULL);
136 assert(username != NULL);
137 assert(password != NULL);
139 /***************************************************************************/
140 /* Store the API URLs. */
141 /***************************************************************************/
142 evel_event_api_url = strdup(event_api_url);
143 assert(evel_event_api_url != NULL);
144 evel_throt_api_url = strdup(throt_api_url);
145 assert(evel_throt_api_url != NULL);
147 /***************************************************************************/
148 /* Start the CURL library. Note that this initialization is not threadsafe */
149 /* which imposes a constraint that the EVEL library is initialized before */
150 /* any threads are started. */
151 /***************************************************************************/
152 curl_rc = curl_global_init(CURL_GLOBAL_SSL);
153 if (curl_rc != CURLE_OK)
155 rc = EVEL_CURL_LIBRARY_FAIL;
156 log_error_state("Failed to initialize libCURL. Error code=%d", curl_rc);
160 /***************************************************************************/
161 /* Get a curl handle which we'll use for all of our output. */
162 /***************************************************************************/
163 curl_handle = curl_easy_init();
164 if (curl_handle == NULL)
166 rc = EVEL_CURL_LIBRARY_FAIL;
167 log_error_state("Failed to get libCURL handle");
171 /***************************************************************************/
172 /* Prime the library to give friendly error codes. */
173 /***************************************************************************/
174 curl_rc = curl_easy_setopt(curl_handle,
177 if (curl_rc != CURLE_OK)
179 rc = EVEL_CURL_LIBRARY_FAIL;
180 log_error_state("Failed to initialize libCURL to provide friendly errors. "
181 "Error code=%d", curl_rc);
185 /***************************************************************************/
186 /* If running in verbose mode generate more output. */
187 /***************************************************************************/
190 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
191 if (curl_rc != CURLE_OK)
193 rc = EVEL_CURL_LIBRARY_FAIL;
194 log_error_state("Failed to initialize libCURL to be verbose. "
195 "Error code=%d", curl_rc);
200 /***************************************************************************/
201 /* Set the URL for the API. */
202 /***************************************************************************/
203 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, event_api_url);
204 if (curl_rc != CURLE_OK)
206 rc = EVEL_CURL_LIBRARY_FAIL;
207 log_error_state("Failed to initialize libCURL with the API URL. "
208 "Error code=%d (%s)", curl_rc, curl_err_string);
211 EVEL_INFO("Initializing CURL to send events to: %s", event_api_url);
213 /***************************************************************************/
214 /* send all data to this function. */
215 /***************************************************************************/
216 curl_rc = curl_easy_setopt(curl_handle,
217 CURLOPT_WRITEFUNCTION,
218 evel_write_callback);
219 if (curl_rc != CURLE_OK)
221 rc = EVEL_CURL_LIBRARY_FAIL;
222 log_error_state("Failed to initialize libCURL with the write callback. "
223 "Error code=%d (%s)", curl_rc, curl_err_string);
227 /***************************************************************************/
228 /* some servers don't like requests that are made without a user-agent */
229 /* field, so we provide one. */
230 /***************************************************************************/
231 curl_rc = curl_easy_setopt(curl_handle,
233 "libcurl-agent/1.0");
234 if (curl_rc != CURLE_OK)
236 rc = EVEL_CURL_LIBRARY_FAIL;
237 log_error_state("Failed to initialize libCURL to upload. "
238 "Error code=%d (%s)", curl_rc, curl_err_string);
242 /***************************************************************************/
243 /* Specify that we are going to POST data. */
244 /***************************************************************************/
245 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);
246 if (curl_rc != CURLE_OK)
248 rc = EVEL_CURL_LIBRARY_FAIL;
249 log_error_state("Failed to initialize libCURL to upload. "
250 "Error code=%d (%s)", curl_rc, curl_err_string);
254 /***************************************************************************/
255 /* we want to use our own read function. */
256 /***************************************************************************/
257 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, read_callback);
258 if (curl_rc != CURLE_OK)
260 rc = EVEL_CURL_LIBRARY_FAIL;
261 log_error_state("Failed to initialize libCURL to upload using read "
262 "function. Error code=%d (%s)", curl_rc, curl_err_string);
266 /***************************************************************************/
267 /* All of our events are JSON encoded. We also suppress the */
268 /* Expect: 100-continue header that we would otherwise get since it */
269 /* confuses some servers. */
271 /* @TODO: do AT&T want this behavior? */
272 /***************************************************************************/
273 hdr_chunk = curl_slist_append(hdr_chunk, "Content-type: application/json");
274 hdr_chunk = curl_slist_append(hdr_chunk, "Expect:");
276 /***************************************************************************/
277 /* set our custom set of headers. */
278 /***************************************************************************/
279 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, hdr_chunk);
280 if (curl_rc != CURLE_OK)
282 rc = EVEL_CURL_LIBRARY_FAIL;
283 log_error_state("Failed to initialize libCURL to use custom headers. "
284 "Error code=%d (%s)", curl_rc, curl_err_string);
288 /***************************************************************************/
289 /* Set the timeout for the operation. */
290 /***************************************************************************/
291 curl_rc = curl_easy_setopt(curl_handle,
294 if (curl_rc != CURLE_OK)
296 rc = EVEL_CURL_LIBRARY_FAIL;
297 log_error_state("Failed to initialize libCURL for API timeout. "
298 "Error code=%d (%s)", curl_rc, curl_err_string);
302 /***************************************************************************/
303 /* Set that we want Basic authentication with username:password Base-64 */
304 /* encoded for the operation. */
305 /***************************************************************************/
306 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
307 if (curl_rc != CURLE_OK)
309 rc = EVEL_CURL_LIBRARY_FAIL;
310 log_error_state("Failed to initialize libCURL for Basic Authentication. "
311 "Error code=%d (%s)", curl_rc, curl_err_string);
314 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_USERNAME, username);
315 if (curl_rc != CURLE_OK)
317 rc = EVEL_CURL_LIBRARY_FAIL;
318 log_error_state("Failed to initialize libCURL with username. "
319 "Error code=%d (%s)", curl_rc, curl_err_string);
322 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_PASSWORD, password);
323 if (curl_rc != CURLE_OK)
325 rc = EVEL_CURL_LIBRARY_FAIL;
326 log_error_state("Failed to initialize libCURL with password. "
327 "Error code=%d (%s)", curl_rc, curl_err_string);
331 /***************************************************************************/
332 /* Initialize a message ring-buffer to be used between the foreground and */
333 /* the thread which sends the messages. This can't fail. */
334 /***************************************************************************/
335 ring_buffer_initialize(&event_buffer, EVEL_EVENT_BUFFER_DEPTH);
337 /***************************************************************************/
338 /* Initialize the priority post buffer to empty. */
339 /***************************************************************************/
340 priority_post.memory = NULL;
348 /**************************************************************************//**
349 * Run the event handler.
351 * Spawns the thread responsible for handling events and sending them to the
354 * @return Status code.
355 * @retval ::EVEL_SUCCESS if everything OK.
356 * @retval One of ::EVEL_ERR_CODES if there was a problem.
357 *****************************************************************************/
358 EVEL_ERR_CODES event_handler_run()
360 EVEL_ERR_CODES rc = EVEL_SUCCESS;
365 /***************************************************************************/
366 /* Start the event handler thread. */
367 /***************************************************************************/
368 evt_handler_state = EVT_HANDLER_INACTIVE;
369 pthread_rc = pthread_create(&evt_handler_thread, NULL, event_handler, NULL);
372 rc = EVEL_PTHREAD_LIBRARY_FAIL;
373 log_error_state("Failed to start event handler thread. "
374 "Error code=%d", pthread_rc);
381 /**************************************************************************//**
382 * Terminate the event handler.
384 * Shuts down the event handler thread in as clean a way as possible. Sets the
385 * global exit flag and then signals the thread to interrupt it since it's
386 * most likely waiting on the ring-buffer.
388 * Having achieved an orderly shutdown of the event handler thread, clean up
389 * the cURL library's resources cleanly.
391 * @return Status code.
392 * @retval ::EVEL_SUCCESS if everything OK.
393 * @retval One of ::EVEL_ERR_CODES if there was a problem.
394 *****************************************************************************/
395 EVEL_ERR_CODES event_handler_terminate()
397 EVEL_ERR_CODES rc = EVEL_SUCCESS;
400 EVENT_INTERNAL *event = NULL;
402 /***************************************************************************/
403 /* Make sure that we were initialized before trying to terminate the */
404 /* event handler thread. */
405 /***************************************************************************/
406 if (evt_handler_state != EVT_HANDLER_UNINITIALIZED)
408 /*************************************************************************/
409 /* Make sure that the event handler knows it's time to die. */
410 /*************************************************************************/
411 event = evel_new_internal_event(EVT_CMD_TERMINATE);
414 /***********************************************************************/
415 /* We failed to get an event, but we don't bail out - we will just */
416 /* clean up what we can and continue on our way, since we're exiting */
418 /***********************************************************************/
419 EVEL_ERROR("Failed to get internal event - perform dirty exit instead!");
423 /***********************************************************************/
424 /* Post the event then wait for the Event Handler to exit. Set the */
425 /* global command, too, in case the ring-buffer is full. */
426 /***********************************************************************/
427 EVEL_DEBUG("Sending event to Event Hander to request it to exit.");
428 evt_handler_state = EVT_HANDLER_REQUEST_TERMINATE;
429 evel_post_event((EVENT_HEADER *) event);
430 pthread_join(evt_handler_thread, NULL);
431 EVEL_DEBUG("Event Handler thread has exited.");
436 EVEL_DEBUG("Event handler was not initialized, so no need to kill it");
439 /***************************************************************************/
440 /* Clean-up the cURL library. */
441 /***************************************************************************/
442 if (curl_handle != NULL)
444 curl_easy_cleanup(curl_handle);
447 if (hdr_chunk != NULL)
449 curl_slist_free_all(hdr_chunk);
453 /***************************************************************************/
454 /* Free off the stored API URL strings. */
455 /***************************************************************************/
456 if (evel_event_api_url != NULL)
458 free(evel_event_api_url);
459 evel_event_api_url = NULL;
461 if (evel_throt_api_url != NULL)
463 free(evel_throt_api_url);
464 evel_throt_api_url = NULL;
471 /**************************************************************************//**
474 * @note So far as the caller is concerned, successfully posting the event
475 * relinquishes all responsibility for the event - the library will take care
476 * of freeing the event in due course.
478 * @param event The event to be posted.
480 * @returns Status code
481 * @retval EVEL_SUCCESS On success
482 * @retval "One of ::EVEL_ERR_CODES" On failure.
483 *****************************************************************************/
484 EVEL_ERR_CODES evel_post_event(EVENT_HEADER * event)
486 int rc = EVEL_SUCCESS;
490 /***************************************************************************/
491 /* Check preconditions. */
492 /***************************************************************************/
493 assert(event != NULL);
495 /***************************************************************************/
496 /* We need to make sure that we are either initializing or running */
497 /* normally before writing the event into the buffer so that we can */
498 /* guarantee that the ring-buffer empties properly on exit. */
499 /***************************************************************************/
500 if ((evt_handler_state == EVT_HANDLER_ACTIVE) ||
501 (evt_handler_state == EVT_HANDLER_INACTIVE) ||
502 (evt_handler_state == EVT_HANDLER_REQUEST_TERMINATE))
504 if (ring_buffer_write(&event_buffer, event) == 0)
506 log_error_state("Failed to write event to buffer - event dropped!");
507 rc = EVEL_EVENT_BUFFER_FULL;
508 evel_free_event(event);
513 /*************************************************************************/
514 /* System is not in active operation, so reject the event. */
515 /*************************************************************************/
516 log_error_state("Event Handler system not active - event dropped!");
517 rc = EVEL_EVENT_HANDLER_INACTIVE;
518 evel_free_event(event);
525 /**************************************************************************//**
526 * Post an event to the Vendor Event Listener API.
528 * @returns Status code
529 * @retval EVEL_SUCCESS On success
530 * @retval "One of ::EVEL_ERR_CODES" On failure.
531 *****************************************************************************/
532 static EVEL_ERR_CODES evel_post_api(char * msg, size_t size)
534 int rc = EVEL_SUCCESS;
535 CURLcode curl_rc = CURLE_OK;
536 MEMORY_CHUNK rx_chunk;
537 MEMORY_CHUNK tx_chunk;
538 int http_response_code = 0;
542 /***************************************************************************/
543 /* Create the memory chunk to be used for the response to the post. The */
544 /* will be realloced. */
545 /***************************************************************************/
546 rx_chunk.memory = malloc(1);
547 assert(rx_chunk.memory != NULL);
550 /***************************************************************************/
551 /* Create the memory chunk to be sent as the body of the post. */
552 /***************************************************************************/
553 tx_chunk.memory = msg;
554 tx_chunk.size = size;
555 EVEL_DEBUG("Sending chunk of size %d", tx_chunk.size);
557 /***************************************************************************/
558 /* Point to the data to be received. */
559 /***************************************************************************/
560 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &rx_chunk);
561 if (curl_rc != CURLE_OK)
563 rc = EVEL_CURL_LIBRARY_FAIL;
564 log_error_state("Failed to initialize libCURL to upload. "
565 "Error code=%d (%s)", curl_rc, curl_err_string);
568 EVEL_DEBUG("Initialized data to receive");
570 /***************************************************************************/
571 /* Pointer to pass to our read function */
572 /***************************************************************************/
573 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_READDATA, &tx_chunk);
574 if (curl_rc != CURLE_OK)
576 rc = EVEL_CURL_LIBRARY_FAIL;
577 log_error_state("Failed to set upload data for libCURL to upload. "
578 "Error code=%d (%s)", curl_rc, curl_err_string);
581 EVEL_DEBUG("Initialized data to send");
583 /***************************************************************************/
584 /* Size of the data to transmit. */
585 /***************************************************************************/
586 curl_rc = curl_easy_setopt(curl_handle,
587 CURLOPT_POSTFIELDSIZE,
589 if (curl_rc != CURLE_OK)
591 rc = EVEL_CURL_LIBRARY_FAIL;
592 log_error_state("Failed to set length of upload data for libCURL to "
593 "upload. Error code=%d (%s)", curl_rc, curl_err_string);
596 EVEL_DEBUG("Initialized length of data to send");
598 /***************************************************************************/
599 /* Now run off and do what you've been told! */
600 /***************************************************************************/
601 curl_rc = curl_easy_perform(curl_handle);
602 if (curl_rc != CURLE_OK)
604 rc = EVEL_CURL_LIBRARY_FAIL;
605 log_error_state("Failed to transfer an event to Vendor Event Listener! "
606 "Error code=%d (%s)", curl_rc, curl_err_string);
607 EVEL_ERROR("Dropped event: %s", msg);
611 /***************************************************************************/
612 /* See what response we got - any 2XX response is good. */
613 /***************************************************************************/
614 curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_response_code);
615 EVEL_DEBUG("HTTP response code: %d", http_response_code);
616 if ((http_response_code / 100) == 2)
618 /*************************************************************************/
619 /* If the server responded with data it may be interesting but not a */
621 /*************************************************************************/
622 if ((rx_chunk.size > 0) && (rx_chunk.memory != NULL))
624 EVEL_DEBUG("Server returned data = %d (%s)",
628 /***********************************************************************/
629 /* If this is a response to priority post, then we're not interested. */
630 /***********************************************************************/
631 if (priority_post.memory != NULL)
633 EVEL_ERROR("Ignoring priority post response");
637 evel_handle_event_response(&rx_chunk, &priority_post);
643 EVEL_ERROR("Unexpected HTTP response code: %d with data size %d (%s)",
646 rx_chunk.size > 0 ? rx_chunk.memory : "NONE");
647 EVEL_ERROR("Potentially dropped event: %s", msg);
651 free(rx_chunk.memory);
656 /**************************************************************************//**
657 * Callback function to provide data to send.
659 * Copy data into the supplied buffer, read_callback::ptr, checking size
662 * @returns Number of bytes placed into read_callback::ptr. 0 for EOF.
663 *****************************************************************************/
664 static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
667 size_t bytes_to_write = 0;
668 MEMORY_CHUNK *tx_chunk = (MEMORY_CHUNK *)userp;
672 bytes_to_write = min(size*nmemb, tx_chunk->size);
674 if (bytes_to_write > 0)
676 EVEL_DEBUG("Going to try to write %d bytes", bytes_to_write);
677 strncpy((char *)ptr, tx_chunk->memory, bytes_to_write);
678 tx_chunk->memory += bytes_to_write;
679 tx_chunk->size -= bytes_to_write;
680 rtn = bytes_to_write;
684 EVEL_DEBUG("Reached EOF");
691 /**************************************************************************//**
692 * Callback function to provide returned data.
694 * Copy data into the supplied buffer, write_callback::ptr, checking size
697 * @returns Number of bytes placed into write_callback::ptr. 0 for EOF.
698 *****************************************************************************/
699 size_t evel_write_callback(void *contents,
704 size_t realsize = size * nmemb;
705 MEMORY_CHUNK * rx_chunk = (MEMORY_CHUNK *)userp;
709 EVEL_DEBUG("Called with %d chunks of %d size = %d", nmemb, size, realsize);
710 EVEL_DEBUG("rx chunk size is %d", rx_chunk->size);
712 rx_chunk->memory = realloc(rx_chunk->memory, rx_chunk->size + realsize + 1);
713 if(rx_chunk->memory == NULL) {
715 printf("not enough memory (realloc returned NULL)\n");
719 memcpy(&(rx_chunk->memory[rx_chunk->size]), contents, realsize);
720 rx_chunk->size += realsize;
721 rx_chunk->memory[rx_chunk->size] = 0;
723 EVEL_DEBUG("Rx data: %s", rx_chunk->memory);
724 EVEL_DEBUG("Returning: %d", realsize);
730 /**************************************************************************//**
733 * Watch for messages coming on the internal queue and send them to the
736 * param[in] arg Argument - unused.
737 *****************************************************************************/
738 static void * event_handler(void * arg __attribute__ ((unused)))
741 EVENT_HEADER * msg = NULL;
742 EVENT_INTERNAL * internal_msg = NULL;
744 char json_body[EVEL_MAX_JSON_BODY];
745 int rc = EVEL_SUCCESS;
748 EVEL_INFO("Event handler thread started");
750 /***************************************************************************/
751 /* Set this thread to be cancellable immediately. */
752 /***************************************************************************/
753 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type);
755 /***************************************************************************/
756 /* Set the handler as active, defending against weird situations like */
757 /* immediately shutting down after initializing the library so the */
758 /* handler never gets started up properly. */
759 /***************************************************************************/
760 if (evt_handler_state == EVT_HANDLER_INACTIVE)
762 evt_handler_state = EVT_HANDLER_ACTIVE;
766 EVEL_ERROR("Event Handler State was not INACTIVE at start-up - "
767 "Handler will exit immediately!");
770 while (evt_handler_state == EVT_HANDLER_ACTIVE)
772 /*************************************************************************/
773 /* Wait for a message to be received. */
774 /*************************************************************************/
775 EVEL_DEBUG("Event handler getting any messages");
776 msg = ring_buffer_read(&event_buffer);
778 /*************************************************************************/
779 /* Internal events get special treatment while regular events get posted */
780 /* to the far side. */
781 /*************************************************************************/
782 if (msg->event_domain != EVEL_DOMAIN_INTERNAL)
784 EVEL_DEBUG("External event received");
786 /***********************************************************************/
787 /* Encode the event in JSON. */
788 /***********************************************************************/
789 json_size = evel_json_encode_event(json_body, EVEL_MAX_JSON_BODY, msg);
791 /***********************************************************************/
792 /* Send the JSON across the API. */
793 /***********************************************************************/
794 EVEL_DEBUG("Sending JSON of size %d is: %s", json_size, json_body);
795 rc = evel_post_api(json_body, json_size);
796 if (rc != EVEL_SUCCESS)
798 EVEL_ERROR("Failed to transfer the data. Error code=%d", rc);
803 EVEL_DEBUG("Internal event received");
804 internal_msg = (EVENT_INTERNAL *) msg;
805 assert(internal_msg->command == EVT_CMD_TERMINATE);
806 evt_handler_state = EVT_HANDLER_TERMINATING;
809 /*************************************************************************/
810 /* We are responsible for freeing the memory. */
811 /*************************************************************************/
812 evel_free_event(msg);
815 /*************************************************************************/
816 /* There may be a single priority post to be sent. */
817 /*************************************************************************/
818 if (priority_post.memory != NULL)
820 EVEL_DEBUG("Priority Post");
822 /***********************************************************************/
823 /* Set the URL for the throttling API. */
824 /***********************************************************************/
825 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_throt_api_url);
826 if (curl_rc != CURLE_OK)
828 /*********************************************************************/
829 /* This is only likely to happen with CURLE_OUT_OF_MEMORY, in which */
830 /* case we carry on regardless. */
831 /*********************************************************************/
832 EVEL_ERROR("Failed to set throttling URL. Error code=%d", rc);
836 rc = evel_post_api(priority_post.memory, priority_post.size);
837 if (rc != EVEL_SUCCESS)
839 EVEL_ERROR("Failed to transfer priority post. Error code=%d", rc);
843 /***********************************************************************/
844 /* Reinstate the URL for the event API. */
845 /***********************************************************************/
846 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_event_api_url);
847 if (curl_rc != CURLE_OK)
849 /*********************************************************************/
850 /* This is only likely to happen with CURLE_OUT_OF_MEMORY, in which */
851 /* case we carry on regardless. */
852 /*********************************************************************/
853 EVEL_ERROR("Failed to reinstate events URL. Error code=%d", rc);
856 /***********************************************************************/
857 /* We are responsible for freeing the memory. */
858 /***********************************************************************/
859 free(priority_post.memory);
860 priority_post.memory = NULL;
864 /***************************************************************************/
865 /* The event handler is now exiting. The ring-buffer could contain events */
866 /* which have not been processed, so deplete those. Because we've been */
867 /* asked to exit we can be confident that the foreground will have stopped */
868 /* sending events in so we know that this process will conclude! */
869 /***************************************************************************/
870 evt_handler_state = EVT_HANDLER_TERMINATING;
871 while (!ring_buffer_is_empty(&event_buffer))
873 EVEL_DEBUG("Reading event from buffer");
874 msg = ring_buffer_read(&event_buffer);
875 evel_free_event(msg);
877 evt_handler_state = EVT_HANDLER_TERMINATED;
878 EVEL_INFO("Event handler thread stopped");
883 /**************************************************************************//**
884 * Handle a JSON response from the listener, contained in a ::MEMORY_CHUNK.
886 * Tokenize the response, and decode any tokens found.
888 * @param chunk The memory chunk containing the response.
889 * @param post The memory chunk in which to place any resulting POST.
890 *****************************************************************************/
891 void evel_handle_event_response(const MEMORY_CHUNK * const chunk,
892 MEMORY_CHUNK * const post)
894 jsmn_parser json_parser;
895 jsmntok_t json_tokens[EVEL_MAX_RESPONSE_TOKENS];
900 /***************************************************************************/
901 /* Check preconditions. */
902 /***************************************************************************/
903 assert(chunk != NULL);
904 assert(priority_post.memory == NULL);
906 EVEL_DEBUG("Response size = %d", chunk->size);
907 EVEL_DEBUG("Response = %s", chunk->memory);
909 /***************************************************************************/
910 /* Initialize the parser and tokenize the response. */
911 /***************************************************************************/
912 jsmn_init(&json_parser);
913 num_tokens = jsmn_parse(&json_parser,
917 EVEL_MAX_RESPONSE_TOKENS);
921 EVEL_ERROR("Failed to parse JSON response. "
922 "Error code=%d", num_tokens);
924 else if (num_tokens == 0)
926 EVEL_DEBUG("No tokens found in JSON response");
930 EVEL_DEBUG("Decode JSON response tokens");
931 if (!evel_handle_response_tokens(chunk, json_tokens, num_tokens, post))
933 EVEL_ERROR("Failed to handle JSON response.");
940 /**************************************************************************//**
941 * Handle a JSON response from the listener, as a list of tokens from JSMN.
943 * @param chunk Memory chunk containing the JSON buffer.
944 * @param json_tokens Array of tokens to handle.
945 * @param num_tokens The number of tokens to handle.
946 * @param post The memory chunk in which to place any resulting POST.
947 * @return true if we handled the response, false otherwise.
948 *****************************************************************************/
949 bool evel_handle_response_tokens(const MEMORY_CHUNK * const chunk,
950 const jsmntok_t * const json_tokens,
951 const int num_tokens,
952 MEMORY_CHUNK * const post)
954 bool json_ok = false;
958 /***************************************************************************/
959 /* Check preconditions. */
960 /***************************************************************************/
961 assert(chunk != NULL);
962 assert(json_tokens != NULL);
963 assert(num_tokens < EVEL_MAX_RESPONSE_TOKENS);
965 /***************************************************************************/
966 /* Peek at the tokens to decide what the response it, then call the */
967 /* appropriate handler to handle it. There is only one handler at this */
969 /***************************************************************************/
970 if (evel_tokens_match_command_list(chunk, json_tokens, num_tokens))
972 json_ok = evel_handle_command_list(chunk, json_tokens, num_tokens, post);
980 /**************************************************************************//**
981 * Determine whether a list of tokens looks like a "commandList" response.
983 * @param chunk Memory chunk containing the JSON buffer.
984 * @param json_tokens Token to check.
985 * @param num_tokens The number of tokens to handle.
986 * @return true if the tokens look like a "commandList" match, or false.
987 *****************************************************************************/
988 bool evel_tokens_match_command_list(const MEMORY_CHUNK * const chunk,
989 const jsmntok_t * const json_tokens,
990 const int num_tokens)
996 /***************************************************************************/
997 /* Make some checks on the basic layout of the commandList. */
998 /***************************************************************************/
999 if ((num_tokens > 3) &&
1000 (json_tokens[0].type == JSMN_OBJECT) &&
1001 (json_tokens[1].type == JSMN_STRING) &&
1002 (json_tokens[2].type == JSMN_ARRAY) &&
1003 (evel_token_equals_string(chunk, &json_tokens[1], "commandList")))
1013 /**************************************************************************//**
1014 * Check that a string token matches a given input string.
1016 * @param chunk Memory chunk containing the JSON buffer.
1017 * @param json_token Token to check.
1018 * @param check_string String to check it against.
1019 * @return true if the strings match, or false.
1020 *****************************************************************************/
1021 bool evel_token_equals_string(const MEMORY_CHUNK * const chunk,
1022 const jsmntok_t * json_token,
1023 const char * check_string)
1025 bool result = false;
1029 const int token_length = json_token->end - json_token->start;
1030 const char * const token_string = chunk->memory + json_token->start;
1032 if (token_length == (int)strlen(check_string))
1034 result = (strncmp(token_string, check_string, token_length) == 0);