1 /**************************************************************************//**
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.
11 * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
13 * Licensed under the Apache License, Version 2.0 (the "License");
14 * you may not use this file except in compliance with the License.
15 * You may obtain a copy of the License at
16 * http://www.apache.org/licenses/LICENSE-2.0
18 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
23 *****************************************************************************/
30 #include <curl/curl.h>
33 #include "evel_internal.h"
34 #include "ring_buffer.h"
35 #include "evel_throttle.h"
37 /**************************************************************************//**
38 * How long we're prepared to wait for the API service to respond in
40 *****************************************************************************/
41 static const int EVEL_API_TIMEOUT = 5;
43 /*****************************************************************************/
44 /* Prototypes of locally scoped functions. */
45 /*****************************************************************************/
46 static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp);
47 static void * event_handler(void *arg);
48 static bool evel_handle_response_tokens(const MEMORY_CHUNK * const chunk,
49 const jsmntok_t * const json_tokens,
51 MEMORY_CHUNK * const post);
52 static bool evel_tokens_match_command_list(const MEMORY_CHUNK * const chunk,
53 const jsmntok_t * const json_token,
54 const int num_tokens);
55 static bool evel_token_equals_string(const MEMORY_CHUNK * const chunk,
56 const jsmntok_t * const json_token,
57 const char * check_string);
59 /**************************************************************************//**
60 * Buffers for error strings from libcurl.
61 *****************************************************************************/
62 static char curl_err_string[CURL_ERROR_SIZE] = "<NULL>";
64 /**************************************************************************//**
65 * Handle for the API into libcurl.
66 *****************************************************************************/
67 static CURL * curl_handle = NULL;
69 /**************************************************************************//**
70 * Special headers that we send.
71 *****************************************************************************/
72 static struct curl_slist * hdr_chunk = NULL;
74 /**************************************************************************//**
75 * Message queue for sending events to the API.
76 *****************************************************************************/
77 static ring_buffer event_buffer;
79 /**************************************************************************//**
80 * Single pending priority post, which can be generated as a result of a
81 * response to an event. Currently only used to respond to a commandList.
82 *****************************************************************************/
83 static MEMORY_CHUNK priority_post;
85 /**************************************************************************//**
86 * The thread which is responsible for handling events off of the ring-buffer
87 * and posting them to the Event Handler API.
88 *****************************************************************************/
89 static pthread_t evt_handler_thread;
91 /**************************************************************************//**
92 * Variable to convey to the event handler thread what the foreground wants it
94 *****************************************************************************/
95 static EVT_HANDLER_STATE evt_handler_state = EVT_HANDLER_UNINITIALIZED;
97 /**************************************************************************//**
98 * The configured API URL for event and throttling.
99 *****************************************************************************/
100 static char * evel_event_api_url;
101 static char * evel_throt_api_url;
103 /**************************************************************************//**
104 * Initialize the event handler.
106 * Primarily responsible for getting CURL ready for use.
108 * @param[in] event_api_url
109 * The URL where the Vendor Event Listener API is expected
111 * @param[in] throt_api_url
112 * The URL where the Throttling API is expected to be.
113 * @param[in] username The username for the Basic Authentication of requests.
114 * @param[in] password The password for the Basic Authentication of requests.
115 * @param verbosity 0 for normal operation, positive values for chattier
117 *****************************************************************************/
118 EVEL_ERR_CODES event_handler_initialize(const char * const event_api_url,
119 const char * const throt_api_url,
120 const char * const username,
121 const char * const password,
124 int rc = EVEL_SUCCESS;
125 CURLcode curl_rc = CURLE_OK;
129 /***************************************************************************/
130 /* Check assumptions. */
131 /***************************************************************************/
132 assert(event_api_url != NULL);
133 assert(throt_api_url != NULL);
134 assert(username != NULL);
135 assert(password != NULL);
137 /***************************************************************************/
138 /* Store the API URLs. */
139 /***************************************************************************/
140 evel_event_api_url = strdup(event_api_url);
141 assert(evel_event_api_url != NULL);
142 evel_throt_api_url = strdup(throt_api_url);
143 assert(evel_throt_api_url != NULL);
145 /***************************************************************************/
146 /* Start the CURL library. Note that this initialization is not threadsafe */
147 /* which imposes a constraint that the EVEL library is initialized before */
148 /* any threads are started. */
149 /***************************************************************************/
150 curl_rc = curl_global_init(CURL_GLOBAL_SSL);
151 if (curl_rc != CURLE_OK)
153 rc = EVEL_CURL_LIBRARY_FAIL;
154 log_error_state("Failed to initialize libCURL. Error code=%d", curl_rc);
158 /***************************************************************************/
159 /* Get a curl handle which we'll use for all of our output. */
160 /***************************************************************************/
161 curl_handle = curl_easy_init();
162 if (curl_handle == NULL)
164 rc = EVEL_CURL_LIBRARY_FAIL;
165 log_error_state("Failed to get libCURL handle");
169 /***************************************************************************/
170 /* Prime the library to give friendly error codes. */
171 /***************************************************************************/
172 curl_rc = curl_easy_setopt(curl_handle,
175 if (curl_rc != CURLE_OK)
177 rc = EVEL_CURL_LIBRARY_FAIL;
178 log_error_state("Failed to initialize libCURL to provide friendly errors. "
179 "Error code=%d", curl_rc);
183 /***************************************************************************/
184 /* If running in verbose mode generate more output. */
185 /***************************************************************************/
188 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
189 if (curl_rc != CURLE_OK)
191 rc = EVEL_CURL_LIBRARY_FAIL;
192 log_error_state("Failed to initialize libCURL to be verbose. "
193 "Error code=%d", curl_rc);
198 /***************************************************************************/
199 /* Set the URL for the API. */
200 /***************************************************************************/
201 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, event_api_url);
202 if (curl_rc != CURLE_OK)
204 rc = EVEL_CURL_LIBRARY_FAIL;
205 log_error_state("Failed to initialize libCURL with the API URL. "
206 "Error code=%d (%s)", curl_rc, curl_err_string);
209 EVEL_INFO("Initializing CURL to send events to: %s", event_api_url);
211 /***************************************************************************/
212 /* send all data to this function. */
213 /***************************************************************************/
214 curl_rc = curl_easy_setopt(curl_handle,
215 CURLOPT_WRITEFUNCTION,
216 evel_write_callback);
217 if (curl_rc != CURLE_OK)
219 rc = EVEL_CURL_LIBRARY_FAIL;
220 log_error_state("Failed to initialize libCURL with the write callback. "
221 "Error code=%d (%s)", curl_rc, curl_err_string);
225 /***************************************************************************/
226 /* some servers don't like requests that are made without a user-agent */
227 /* field, so we provide one. */
228 /***************************************************************************/
229 curl_rc = curl_easy_setopt(curl_handle,
231 "libcurl-agent/1.0");
232 if (curl_rc != CURLE_OK)
234 rc = EVEL_CURL_LIBRARY_FAIL;
235 log_error_state("Failed to initialize libCURL to upload. "
236 "Error code=%d (%s)", curl_rc, curl_err_string);
240 /***************************************************************************/
241 /* Specify that we are going to POST data. */
242 /***************************************************************************/
243 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);
244 if (curl_rc != CURLE_OK)
246 rc = EVEL_CURL_LIBRARY_FAIL;
247 log_error_state("Failed to initialize libCURL to upload. "
248 "Error code=%d (%s)", curl_rc, curl_err_string);
252 /***************************************************************************/
253 /* we want to use our own read function. */
254 /***************************************************************************/
255 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, read_callback);
256 if (curl_rc != CURLE_OK)
258 rc = EVEL_CURL_LIBRARY_FAIL;
259 log_error_state("Failed to initialize libCURL to upload using read "
260 "function. Error code=%d (%s)", curl_rc, curl_err_string);
264 /***************************************************************************/
265 /* All of our events are JSON encoded. We also suppress the */
266 /* Expect: 100-continue header that we would otherwise get since it */
267 /* confuses some servers. */
269 /* @TODO: do AT&T want this behavior? */
270 /***************************************************************************/
271 hdr_chunk = curl_slist_append(hdr_chunk, "Content-type: application/json");
272 hdr_chunk = curl_slist_append(hdr_chunk, "Expect:");
274 /***************************************************************************/
275 /* set our custom set of headers. */
276 /***************************************************************************/
277 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, hdr_chunk);
278 if (curl_rc != CURLE_OK)
280 rc = EVEL_CURL_LIBRARY_FAIL;
281 log_error_state("Failed to initialize libCURL to use custom headers. "
282 "Error code=%d (%s)", curl_rc, curl_err_string);
286 /***************************************************************************/
287 /* Set the timeout for the operation. */
288 /***************************************************************************/
289 curl_rc = curl_easy_setopt(curl_handle,
292 if (curl_rc != CURLE_OK)
294 rc = EVEL_CURL_LIBRARY_FAIL;
295 log_error_state("Failed to initialize libCURL for API timeout. "
296 "Error code=%d (%s)", curl_rc, curl_err_string);
300 /***************************************************************************/
301 /* Set that we want Basic authentication with username:password Base-64 */
302 /* encoded for the operation. */
303 /***************************************************************************/
304 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
305 if (curl_rc != CURLE_OK)
307 rc = EVEL_CURL_LIBRARY_FAIL;
308 log_error_state("Failed to initialize libCURL for Basic Authentication. "
309 "Error code=%d (%s)", curl_rc, curl_err_string);
312 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_USERNAME, username);
313 if (curl_rc != CURLE_OK)
315 rc = EVEL_CURL_LIBRARY_FAIL;
316 log_error_state("Failed to initialize libCURL with username. "
317 "Error code=%d (%s)", curl_rc, curl_err_string);
320 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_PASSWORD, password);
321 if (curl_rc != CURLE_OK)
323 rc = EVEL_CURL_LIBRARY_FAIL;
324 log_error_state("Failed to initialize libCURL with password. "
325 "Error code=%d (%s)", curl_rc, curl_err_string);
329 /***************************************************************************/
330 /* Initialize a message ring-buffer to be used between the foreground and */
331 /* the thread which sends the messages. This can't fail. */
332 /***************************************************************************/
333 ring_buffer_initialize(&event_buffer, EVEL_EVENT_BUFFER_DEPTH);
335 /***************************************************************************/
336 /* Initialize the priority post buffer to empty. */
337 /***************************************************************************/
338 priority_post.memory = NULL;
346 /**************************************************************************//**
347 * Run the event handler.
349 * Spawns the thread responsible for handling events and sending them to the
352 * @return Status code.
353 * @retval ::EVEL_SUCCESS if everything OK.
354 * @retval One of ::EVEL_ERR_CODES if there was a problem.
355 *****************************************************************************/
356 EVEL_ERR_CODES event_handler_run()
358 EVEL_ERR_CODES rc = EVEL_SUCCESS;
363 /***************************************************************************/
364 /* Start the event handler thread. */
365 /***************************************************************************/
366 evt_handler_state = EVT_HANDLER_INACTIVE;
367 pthread_rc = pthread_create(&evt_handler_thread, NULL, event_handler, NULL);
370 rc = EVEL_PTHREAD_LIBRARY_FAIL;
371 log_error_state("Failed to start event handler thread. "
372 "Error code=%d", pthread_rc);
379 /**************************************************************************//**
380 * Terminate the event handler.
382 * Shuts down the event handler thread in as clean a way as possible. Sets the
383 * global exit flag and then signals the thread to interrupt it since it's
384 * most likely waiting on the ring-buffer.
386 * Having achieved an orderly shutdown of the event handler thread, clean up
387 * the cURL library's resources cleanly.
389 * @return Status code.
390 * @retval ::EVEL_SUCCESS if everything OK.
391 * @retval One of ::EVEL_ERR_CODES if there was a problem.
392 *****************************************************************************/
393 EVEL_ERR_CODES event_handler_terminate()
395 EVEL_ERR_CODES rc = EVEL_SUCCESS;
398 EVENT_INTERNAL *event = NULL;
400 /***************************************************************************/
401 /* Make sure that we were initialized before trying to terminate the */
402 /* event handler thread. */
403 /***************************************************************************/
404 if (evt_handler_state != EVT_HANDLER_UNINITIALIZED)
406 /*************************************************************************/
407 /* Make sure that the event handler knows it's time to die. */
408 /*************************************************************************/
409 event = evel_new_internal_event(EVT_CMD_TERMINATE);
412 /***********************************************************************/
413 /* We failed to get an event, but we don't bail out - we will just */
414 /* clean up what we can and continue on our way, since we're exiting */
416 /***********************************************************************/
417 EVEL_ERROR("Failed to get internal event - perform dirty exit instead!");
421 /***********************************************************************/
422 /* Post the event then wait for the Event Handler to exit. Set the */
423 /* global command, too, in case the ring-buffer is full. */
424 /***********************************************************************/
425 EVEL_DEBUG("Sending event to Event Hander to request it to exit.");
426 evt_handler_state = EVT_HANDLER_REQUEST_TERMINATE;
427 evel_post_event((EVENT_HEADER *) event);
428 pthread_join(evt_handler_thread, NULL);
429 EVEL_DEBUG("Event Handler thread has exited.");
434 EVEL_DEBUG("Event handler was not initialized, so no need to kill it");
437 /***************************************************************************/
438 /* Clean-up the cURL library. */
439 /***************************************************************************/
440 if (curl_handle != NULL)
442 curl_easy_cleanup(curl_handle);
445 if (hdr_chunk != NULL)
447 curl_slist_free_all(hdr_chunk);
451 /***************************************************************************/
452 /* Free off the stored API URL strings. */
453 /***************************************************************************/
454 if (evel_event_api_url != NULL)
456 free(evel_event_api_url);
457 evel_event_api_url = NULL;
459 if (evel_throt_api_url != NULL)
461 free(evel_throt_api_url);
462 evel_throt_api_url = NULL;
469 /**************************************************************************//**
472 * @note So far as the caller is concerned, successfully posting the event
473 * relinquishes all responsibility for the event - the library will take care
474 * of freeing the event in due course.
476 * @param event The event to be posted.
478 * @returns Status code
479 * @retval EVEL_SUCCESS On success
480 * @retval "One of ::EVEL_ERR_CODES" On failure.
481 *****************************************************************************/
482 EVEL_ERR_CODES evel_post_event(EVENT_HEADER * event)
484 int rc = EVEL_SUCCESS;
488 /***************************************************************************/
489 /* Check preconditions. */
490 /***************************************************************************/
491 assert(event != NULL);
493 /***************************************************************************/
494 /* We need to make sure that we are either initializing or running */
495 /* normally before writing the event into the buffer so that we can */
496 /* guarantee that the ring-buffer empties properly on exit. */
497 /***************************************************************************/
498 if ((evt_handler_state == EVT_HANDLER_ACTIVE) ||
499 (evt_handler_state == EVT_HANDLER_INACTIVE) ||
500 (evt_handler_state == EVT_HANDLER_REQUEST_TERMINATE))
502 if (ring_buffer_write(&event_buffer, event) == 0)
504 log_error_state("Failed to write event to buffer - event dropped!");
505 rc = EVEL_EVENT_BUFFER_FULL;
506 evel_free_event(event);
511 /*************************************************************************/
512 /* System is not in active operation, so reject the event. */
513 /*************************************************************************/
514 log_error_state("Event Handler system not active - event dropped!");
515 rc = EVEL_EVENT_HANDLER_INACTIVE;
516 evel_free_event(event);
523 /**************************************************************************//**
524 * Post an event to the Vendor Event Listener API.
526 * @returns Status code
527 * @retval EVEL_SUCCESS On success
528 * @retval "One of ::EVEL_ERR_CODES" On failure.
529 *****************************************************************************/
530 static EVEL_ERR_CODES evel_post_api(char * msg, size_t size)
532 int rc = EVEL_SUCCESS;
533 CURLcode curl_rc = CURLE_OK;
534 MEMORY_CHUNK rx_chunk;
535 MEMORY_CHUNK tx_chunk;
536 int http_response_code = 0;
540 /***************************************************************************/
541 /* Create the memory chunk to be used for the response to the post. The */
542 /* will be realloced. */
543 /***************************************************************************/
544 rx_chunk.memory = malloc(1);
545 assert(rx_chunk.memory != NULL);
548 /***************************************************************************/
549 /* Create the memory chunk to be sent as the body of the post. */
550 /***************************************************************************/
551 tx_chunk.memory = msg;
552 tx_chunk.size = size;
553 EVEL_DEBUG("Sending chunk of size %d", tx_chunk.size);
555 /***************************************************************************/
556 /* Point to the data to be received. */
557 /***************************************************************************/
558 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &rx_chunk);
559 if (curl_rc != CURLE_OK)
561 rc = EVEL_CURL_LIBRARY_FAIL;
562 log_error_state("Failed to initialize libCURL to upload. "
563 "Error code=%d (%s)", curl_rc, curl_err_string);
566 EVEL_DEBUG("Initialized data to receive");
568 /***************************************************************************/
569 /* Pointer to pass to our read function */
570 /***************************************************************************/
571 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_READDATA, &tx_chunk);
572 if (curl_rc != CURLE_OK)
574 rc = EVEL_CURL_LIBRARY_FAIL;
575 log_error_state("Failed to set upload data for libCURL to upload. "
576 "Error code=%d (%s)", curl_rc, curl_err_string);
579 EVEL_DEBUG("Initialized data to send");
581 /***************************************************************************/
582 /* Size of the data to transmit. */
583 /***************************************************************************/
584 curl_rc = curl_easy_setopt(curl_handle,
585 CURLOPT_POSTFIELDSIZE,
587 if (curl_rc != CURLE_OK)
589 rc = EVEL_CURL_LIBRARY_FAIL;
590 log_error_state("Failed to set length of upload data for libCURL to "
591 "upload. Error code=%d (%s)", curl_rc, curl_err_string);
594 EVEL_DEBUG("Initialized length of data to send");
596 /***************************************************************************/
597 /* Now run off and do what you've been told! */
598 /***************************************************************************/
599 curl_rc = curl_easy_perform(curl_handle);
600 if (curl_rc != CURLE_OK)
602 rc = EVEL_CURL_LIBRARY_FAIL;
603 log_error_state("Failed to transfer an event to Vendor Event Listener! "
604 "Error code=%d (%s)", curl_rc, curl_err_string);
605 EVEL_ERROR("Dropped event: %s", msg);
609 /***************************************************************************/
610 /* See what response we got - any 2XX response is good. */
611 /***************************************************************************/
612 curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_response_code);
613 EVEL_DEBUG("HTTP response code: %d", http_response_code);
614 if ((http_response_code / 100) == 2)
616 /*************************************************************************/
617 /* If the server responded with data it may be interesting but not a */
619 /*************************************************************************/
620 if ((rx_chunk.size > 0) && (rx_chunk.memory != NULL))
622 EVEL_DEBUG("Server returned data = %d (%s)",
626 /***********************************************************************/
627 /* If this is a response to priority post, then we're not interested. */
628 /***********************************************************************/
629 if (priority_post.memory != NULL)
631 EVEL_ERROR("Ignoring priority post response");
635 evel_handle_event_response(&rx_chunk, &priority_post);
641 EVEL_ERROR("Unexpected HTTP response code: %d with data size %d (%s)",
644 rx_chunk.size > 0 ? rx_chunk.memory : "NONE");
645 EVEL_ERROR("Potentially dropped event: %s", msg);
649 free(rx_chunk.memory);
654 /**************************************************************************//**
655 * Callback function to provide data to send.
657 * Copy data into the supplied buffer, read_callback::ptr, checking size
660 * @returns Number of bytes placed into read_callback::ptr. 0 for EOF.
661 *****************************************************************************/
662 static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
665 size_t bytes_to_write = 0;
666 MEMORY_CHUNK *tx_chunk = (MEMORY_CHUNK *)userp;
670 bytes_to_write = min(size*nmemb, tx_chunk->size);
672 if (bytes_to_write > 0)
674 EVEL_DEBUG("Going to try to write %d bytes", bytes_to_write);
675 strncpy((char *)ptr, tx_chunk->memory, bytes_to_write);
676 tx_chunk->memory += bytes_to_write;
677 tx_chunk->size -= bytes_to_write;
678 rtn = bytes_to_write;
682 EVEL_DEBUG("Reached EOF");
689 /**************************************************************************//**
690 * Callback function to provide returned data.
692 * Copy data into the supplied buffer, write_callback::ptr, checking size
695 * @returns Number of bytes placed into write_callback::ptr. 0 for EOF.
696 *****************************************************************************/
697 size_t evel_write_callback(void *contents,
702 size_t realsize = size * nmemb;
703 MEMORY_CHUNK * rx_chunk = (MEMORY_CHUNK *)userp;
707 EVEL_DEBUG("Called with %d chunks of %d size = %d", nmemb, size, realsize);
708 EVEL_DEBUG("rx chunk size is %d", rx_chunk->size);
710 rx_chunk->memory = realloc(rx_chunk->memory, rx_chunk->size + realsize + 1);
711 if(rx_chunk->memory == NULL) {
713 printf("not enough memory (realloc returned NULL)\n");
717 memcpy(&(rx_chunk->memory[rx_chunk->size]), contents, realsize);
718 rx_chunk->size += realsize;
719 rx_chunk->memory[rx_chunk->size] = 0;
721 EVEL_DEBUG("Rx data: %s", rx_chunk->memory);
722 EVEL_DEBUG("Returning: %d", realsize);
728 /**************************************************************************//**
731 * Watch for messages coming on the internal queue and send them to the
734 * param[in] arg Argument - unused.
735 *****************************************************************************/
736 static void * event_handler(void * arg __attribute__ ((unused)))
739 EVENT_HEADER * msg = NULL;
740 EVENT_INTERNAL * internal_msg = NULL;
742 char json_body[EVEL_MAX_JSON_BODY];
743 int rc = EVEL_SUCCESS;
746 EVEL_INFO("Event handler thread started");
748 /***************************************************************************/
749 /* Set this thread to be cancellable immediately. */
750 /***************************************************************************/
751 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type);
753 /***************************************************************************/
754 /* Set the handler as active, defending against weird situations like */
755 /* immediately shutting down after initializing the library so the */
756 /* handler never gets started up properly. */
757 /***************************************************************************/
758 if (evt_handler_state == EVT_HANDLER_INACTIVE)
760 evt_handler_state = EVT_HANDLER_ACTIVE;
764 EVEL_ERROR("Event Handler State was not INACTIVE at start-up - "
765 "Handler will exit immediately!");
768 while (evt_handler_state == EVT_HANDLER_ACTIVE)
770 /*************************************************************************/
771 /* Wait for a message to be received. */
772 /*************************************************************************/
773 EVEL_DEBUG("Event handler getting any messages");
774 msg = ring_buffer_read(&event_buffer);
776 /*************************************************************************/
777 /* Internal events get special treatment while regular events get posted */
778 /* to the far side. */
779 /*************************************************************************/
780 if (msg->event_domain != EVEL_DOMAIN_INTERNAL)
782 EVEL_DEBUG("External event received");
784 /***********************************************************************/
785 /* Encode the event in JSON. */
786 /***********************************************************************/
787 json_size = evel_json_encode_event(json_body, EVEL_MAX_JSON_BODY, msg);
789 /***********************************************************************/
790 /* Send the JSON across the API. */
791 /***********************************************************************/
792 EVEL_DEBUG("Sending JSON of size %d is: %s", json_size, json_body);
793 rc = evel_post_api(json_body, json_size);
794 if (rc != EVEL_SUCCESS)
796 EVEL_ERROR("Failed to transfer the data. Error code=%d", rc);
801 EVEL_DEBUG("Internal event received");
802 internal_msg = (EVENT_INTERNAL *) msg;
803 assert(internal_msg->command == EVT_CMD_TERMINATE);
804 evt_handler_state = EVT_HANDLER_TERMINATING;
807 /*************************************************************************/
808 /* We are responsible for freeing the memory. */
809 /*************************************************************************/
810 evel_free_event(msg);
813 /*************************************************************************/
814 /* There may be a single priority post to be sent. */
815 /*************************************************************************/
816 if (priority_post.memory != NULL)
818 EVEL_DEBUG("Priority Post");
820 /***********************************************************************/
821 /* Set the URL for the throttling API. */
822 /***********************************************************************/
823 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_throt_api_url);
824 if (curl_rc != CURLE_OK)
826 /*********************************************************************/
827 /* This is only likely to happen with CURLE_OUT_OF_MEMORY, in which */
828 /* case we carry on regardless. */
829 /*********************************************************************/
830 EVEL_ERROR("Failed to set throttling URL. Error code=%d", rc);
834 rc = evel_post_api(priority_post.memory, priority_post.size);
835 if (rc != EVEL_SUCCESS)
837 EVEL_ERROR("Failed to transfer priority post. Error code=%d", rc);
841 /***********************************************************************/
842 /* Reinstate the URL for the event API. */
843 /***********************************************************************/
844 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_event_api_url);
845 if (curl_rc != CURLE_OK)
847 /*********************************************************************/
848 /* This is only likely to happen with CURLE_OUT_OF_MEMORY, in which */
849 /* case we carry on regardless. */
850 /*********************************************************************/
851 EVEL_ERROR("Failed to reinstate events URL. Error code=%d", rc);
854 /***********************************************************************/
855 /* We are responsible for freeing the memory. */
856 /***********************************************************************/
857 free(priority_post.memory);
858 priority_post.memory = NULL;
862 /***************************************************************************/
863 /* The event handler is now exiting. The ring-buffer could contain events */
864 /* which have not been processed, so deplete those. Because we've been */
865 /* asked to exit we can be confident that the foreground will have stopped */
866 /* sending events in so we know that this process will conclude! */
867 /***************************************************************************/
868 evt_handler_state = EVT_HANDLER_TERMINATING;
869 while (!ring_buffer_is_empty(&event_buffer))
871 EVEL_DEBUG("Reading event from buffer");
872 msg = ring_buffer_read(&event_buffer);
873 evel_free_event(msg);
875 evt_handler_state = EVT_HANDLER_TERMINATED;
876 EVEL_INFO("Event handler thread stopped");
881 /**************************************************************************//**
882 * Handle a JSON response from the listener, contained in a ::MEMORY_CHUNK.
884 * Tokenize the response, and decode any tokens found.
886 * @param chunk The memory chunk containing the response.
887 * @param post The memory chunk in which to place any resulting POST.
888 *****************************************************************************/
889 void evel_handle_event_response(const MEMORY_CHUNK * const chunk,
890 MEMORY_CHUNK * const post)
892 jsmn_parser json_parser;
893 jsmntok_t json_tokens[EVEL_MAX_RESPONSE_TOKENS];
898 /***************************************************************************/
899 /* Check preconditions. */
900 /***************************************************************************/
901 assert(chunk != NULL);
902 assert(priority_post.memory == NULL);
904 EVEL_DEBUG("Response size = %d", chunk->size);
905 EVEL_DEBUG("Response = %s", chunk->memory);
907 /***************************************************************************/
908 /* Initialize the parser and tokenize the response. */
909 /***************************************************************************/
910 jsmn_init(&json_parser);
911 num_tokens = jsmn_parse(&json_parser,
915 EVEL_MAX_RESPONSE_TOKENS);
919 EVEL_ERROR("Failed to parse JSON response. "
920 "Error code=%d", num_tokens);
922 else if (num_tokens == 0)
924 EVEL_DEBUG("No tokens found in JSON response");
928 EVEL_DEBUG("Decode JSON response tokens");
929 if (!evel_handle_response_tokens(chunk, json_tokens, num_tokens, post))
931 EVEL_ERROR("Failed to handle JSON response.");
938 /**************************************************************************//**
939 * Handle a JSON response from the listener, as a list of tokens from JSMN.
941 * @param chunk Memory chunk containing the JSON buffer.
942 * @param json_tokens Array of tokens to handle.
943 * @param num_tokens The number of tokens to handle.
944 * @param post The memory chunk in which to place any resulting POST.
945 * @return true if we handled the response, false otherwise.
946 *****************************************************************************/
947 bool evel_handle_response_tokens(const MEMORY_CHUNK * const chunk,
948 const jsmntok_t * const json_tokens,
949 const int num_tokens,
950 MEMORY_CHUNK * const post)
952 bool json_ok = false;
956 /***************************************************************************/
957 /* Check preconditions. */
958 /***************************************************************************/
959 assert(chunk != NULL);
960 assert(json_tokens != NULL);
961 assert(num_tokens < EVEL_MAX_RESPONSE_TOKENS);
963 /***************************************************************************/
964 /* Peek at the tokens to decide what the response it, then call the */
965 /* appropriate handler to handle it. There is only one handler at this */
967 /***************************************************************************/
968 if (evel_tokens_match_command_list(chunk, json_tokens, num_tokens))
970 json_ok = evel_handle_command_list(chunk, json_tokens, num_tokens, post);
978 /**************************************************************************//**
979 * Determine whether a list of tokens looks like a "commandList" response.
981 * @param chunk Memory chunk containing the JSON buffer.
982 * @param json_tokens Token to check.
983 * @param num_tokens The number of tokens to handle.
984 * @return true if the tokens look like a "commandList" match, or false.
985 *****************************************************************************/
986 bool evel_tokens_match_command_list(const MEMORY_CHUNK * const chunk,
987 const jsmntok_t * const json_tokens,
988 const int num_tokens)
994 /***************************************************************************/
995 /* Make some checks on the basic layout of the commandList. */
996 /***************************************************************************/
997 if ((num_tokens > 3) &&
998 (json_tokens[0].type == JSMN_OBJECT) &&
999 (json_tokens[1].type == JSMN_STRING) &&
1000 (json_tokens[2].type == JSMN_ARRAY) &&
1001 (evel_token_equals_string(chunk, &json_tokens[1], "commandList")))
1011 /**************************************************************************//**
1012 * Check that a string token matches a given input string.
1014 * @param chunk Memory chunk containing the JSON buffer.
1015 * @param json_token Token to check.
1016 * @param check_string String to check it against.
1017 * @return true if the strings match, or false.
1018 *****************************************************************************/
1019 bool evel_token_equals_string(const MEMORY_CHUNK * const chunk,
1020 const jsmntok_t * json_token,
1021 const char * check_string)
1023 bool result = false;
1027 const int token_length = json_token->end - json_token->start;
1028 const char * const token_string = chunk->memory + json_token->start;
1030 if (token_length == (int)strlen(check_string))
1032 result = (strncmp(token_string, check_string, token_length) == 0);