1 /*************************************************************************//**
3 * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
5 * Unless otherwise specified, all software contained herein is
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
17 ****************************************************************************/
19 /**************************************************************************//**
23 * Simple event manager that is responsible for taking events (Heartbeats,
24 * Faults and Measurements) from the ring-buffer and posting them to the API.
26 ****************************************************************************/
33 #include <curl/curl.h>
36 #include "evel_internal.h"
37 #include "ring_buffer.h"
38 #include "evel_throttle.h"
40 /**************************************************************************//**
41 * How long we're prepared to wait for the API service to respond in
43 *****************************************************************************/
44 static const int EVEL_API_TIMEOUT = 10;
46 /*****************************************************************************/
47 /* Prototypes of locally scoped functions. */
48 /*****************************************************************************/
49 static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp);
50 static void * event_handler(void *arg);
51 static bool evel_handle_response_tokens(const MEMORY_CHUNK * const chunk,
52 const jsmntok_t * const json_tokens,
54 MEMORY_CHUNK * const post);
55 static bool evel_tokens_match_command_list(const MEMORY_CHUNK * const chunk,
56 const jsmntok_t * const json_token,
57 const int num_tokens);
58 static bool evel_token_equals_string(const MEMORY_CHUNK * const chunk,
59 const jsmntok_t * const json_token,
60 const char * check_string);
61 static void * event_multi_handler(void * arg __attribute__ ((unused)));
63 /**************************************************************************//**
64 * Buffers for error strings from libcurl.
65 *****************************************************************************/
66 static char curl_err_string[CURL_ERROR_SIZE] = "<NULL>";
67 static char curl_err_string2[CURL_ERROR_SIZE] = "<NULL>";
69 /**************************************************************************//**
70 * Handle for the API into libcurl.
71 *****************************************************************************/
72 static CURL * curl_handle = NULL;
73 static CURL * curl_handle2 = NULL;
74 static CURLM * multi_handle = NULL;
75 int curr_global_handles = 0;
78 /**************************************************************************//**
79 * Special headers that we send.
80 *****************************************************************************/
81 static struct curl_slist * hdr_chunk = NULL;
82 static struct curl_slist * hdr_chunk2 = NULL;
84 /**************************************************************************//**
85 * Message queue for sending events to the API.
86 *****************************************************************************/
87 static ring_buffer event_buffer;
89 /**************************************************************************//**
90 * Single pending priority post, which can be generated as a result of a
91 * response to an event. Currently only used to respond to a commandList.
92 *****************************************************************************/
93 static MEMORY_CHUNK priority_post;
95 /**************************************************************************//**
96 * The thread which is responsible for handling events off of the ring-buffer
97 * and posting them to the Event Handler API.
98 *****************************************************************************/
99 static pthread_t evt_handler_thread;
101 /**************************************************************************//**
102 * Variable to convey to the event handler thread what the foreground wants it
104 *****************************************************************************/
105 static EVT_HANDLER_STATE evt_handler_state = EVT_HANDLER_UNINITIALIZED;
107 /**************************************************************************//**
108 * The configured API URL for event and throttling.
109 *****************************************************************************/
110 static char * evel_event_api_url;
111 static char * evel_throt_api_url;
112 static char * evel_batch_api_url;
114 static char * evel_bevent_api_url;
115 static char * evel_bthrot_api_url;
116 static char * evel_bbatch_api_url;
119 /**************************************************************************//**
120 * Initialize the event handler.
122 * Primarily responsible for getting CURL ready for use.
124 * @param[in] event_api_url
125 * The URL where the Vendor Event Listener API is expected
127 * @param[in] throt_api_url
128 * The URL where the Throttling API is expected to be.
129 * @param[in] source_ip Source IP of VES Agent
130 * @param[in] ring_buf_size Initial size of ring buffer
131 * @param[in] secure Whether Using http or https
132 * @param[in] cert_file_path Path to Client Certificate file
133 * @param[in] key_file_path Path to Client key file
134 * @param[in] ca_info Path to CA info file
135 * @param[in] ca_file_path Path to CA file
136 * @param[in] verify_peer Using peer verification or not 0 or 1
137 * @param[in] verify_host Using host verification or not 0 or 1
138 * @param[in] username The username for the Basic Authentication of requests.
139 * @param[in] password The password for the Basic Authentication of requests.
140 * @param verbosity 0 for normal operation, positive values for chattier
142 *****************************************************************************/
143 EVEL_ERR_CODES event_handler_initialize(const char * const event_api_url,
144 const char * const bakup_api_url,
145 const char * const throt_api_url,
146 const char * const source_ip,
147 const char * const source_ip_bakup,
151 const char * const cert_file_path,
152 const char * const key_file_path,
153 const char * const ca_info,
154 const char * const ca_file_path,
157 const char * const username,
158 const char * const password,
159 const char * const username2,
160 const char * const password2,
163 int rc = EVEL_SUCCESS;
164 CURLcode curl_rc = CURLE_OK;
165 char batch_api_url[EVEL_MAX_URL_LEN + 1] = {0};
166 char local_address[64];
170 /***************************************************************************/
171 /* Check assumptions. */
172 /***************************************************************************/
173 assert(event_api_url != NULL);
174 assert(throt_api_url != NULL);
175 assert(username != NULL);
176 assert(password != NULL);
177 if( bakup_api_url != NULL )
179 assert(username2 != NULL);
180 assert(password2 != NULL);
184 /***************************************************************************/
185 /* Store the API URLs. */
186 /***************************************************************************/
187 evel_event_api_url = strdup(event_api_url);
188 assert(evel_event_api_url != NULL);
189 sprintf(batch_api_url,"%s/eventBatch",event_api_url);
190 evel_batch_api_url = strdup(batch_api_url);
191 assert(evel_batch_api_url != NULL);
192 evel_throt_api_url = strdup(throt_api_url);
193 assert(evel_throt_api_url != NULL);
194 curr_global_handles = 1;
196 if( bakup_api_url != NULL )
198 evel_bevent_api_url = strdup(bakup_api_url);
199 assert(evel_bevent_api_url != NULL);
200 sprintf(batch_api_url,"%s/eventBatch",event_api_url);
201 evel_bbatch_api_url = strdup(batch_api_url);
202 assert(evel_bbatch_api_url != NULL);
203 evel_bthrot_api_url = strdup(throt_api_url);
204 assert(evel_bthrot_api_url != NULL);
205 curr_global_handles = 2;
209 curl_version_info_data *d = curl_version_info(CURLVERSION_NOW);
210 /* compare with the 24 bit hex number in 8 bit fields */
211 if(d->version_num >= 0x072100) {
212 /* this is libcurl 7.33.0 or later */
213 EVEL_INFO("7.33 or later Curl version %x.",d->version_num);
216 EVEL_INFO("Old Curl version.");
218 /***************************************************************************/
219 /* Start the CURL library. Note that this initialization is not threadsafe */
220 /* which imposes a constraint that the EVEL library is initialized before */
221 /* any threads are started. */
222 /***************************************************************************/
223 curl_rc = curl_global_init(CURL_GLOBAL_SSL);
224 if (curl_rc != CURLE_OK)
226 rc = EVEL_CURL_LIBRARY_FAIL;
227 log_error_state("Failed to initialize libCURL. Error code=%d", curl_rc);
231 /***************************************************************************/
232 /* Get a curl handle which we'll use for all of our output. */
233 /***************************************************************************/
234 curl_handle = curl_easy_init();
235 if (curl_handle == NULL)
237 rc = EVEL_CURL_LIBRARY_FAIL;
238 log_error_state("Failed to get libCURL handle");
242 if( bakup_api_url != NULL )
244 curl_handle2 = curl_easy_init();
245 if (curl_handle2 == NULL)
247 rc = EVEL_CURL_LIBRARY_FAIL;
248 log_error_state("Failed to get backup libCURL handle");
252 /***************************************************************************/
253 /* Prime the library to give friendly error codes. */
254 /***************************************************************************/
255 curl_rc = curl_easy_setopt(curl_handle,
258 if (curl_rc != CURLE_OK)
260 rc = EVEL_CURL_LIBRARY_FAIL;
261 log_error_state("Failed to initialize libCURL to provide friendly errors. "
262 "Error code=%d", curl_rc);
266 if( bakup_api_url != NULL )
268 curl_rc = curl_easy_setopt(curl_handle2,
271 if (curl_rc != CURLE_OK)
273 rc = EVEL_CURL_LIBRARY_FAIL;
274 log_error_state("Failed to initialize libCURL2 to provide friendly errors. "
275 "Error code=%d", curl_rc);
281 /***************************************************************************/
282 /* If running in verbose mode generate more output. */
283 /***************************************************************************/
286 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
287 if (curl_rc != CURLE_OK)
289 rc = EVEL_CURL_LIBRARY_FAIL;
290 log_error_state("Failed to initialize libCURL to be verbose. "
291 "Error code=%d", curl_rc);
294 if( bakup_api_url != NULL )
296 curl_rc = curl_easy_setopt(curl_handle2, CURLOPT_VERBOSE, 1L);
297 if (curl_rc != CURLE_OK)
299 rc = EVEL_CURL_LIBRARY_FAIL;
300 log_error_state("Failed to initialize libCURL to be verbose. "
301 "Error code=%d", curl_rc);
310 /***************************************************************************/
311 /* Set the URL for the API. */
312 /***************************************************************************/
313 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, event_api_url);
314 if (curl_rc != CURLE_OK)
316 rc = EVEL_CURL_LIBRARY_FAIL;
317 log_error_state("Failed to initialize libCURL with the API URL. "
318 "Error code=%d (%s)", curl_rc, curl_err_string);
321 EVEL_INFO("Initializing CURL to send events to: %s", event_api_url);
323 if( bakup_api_url != NULL )
325 curl_rc = curl_easy_setopt(curl_handle2,
328 if (curl_rc != CURLE_OK)
330 rc = EVEL_CURL_LIBRARY_FAIL;
331 log_error_state("Failed to initialize libCURL with the API URL. "
332 "Error code=%d (%s)", curl_rc, curl_err_string2);
338 /***************************************************************************/
339 /* send all data to this function. */
340 /***************************************************************************/
341 curl_rc = curl_easy_setopt(curl_handle,
342 CURLOPT_WRITEFUNCTION,
343 evel_write_callback);
344 if (curl_rc != CURLE_OK)
346 rc = EVEL_CURL_LIBRARY_FAIL;
347 log_error_state("Failed to initialize libCURL with the write callback. "
348 "Error code=%d (%s)", curl_rc, curl_err_string);
351 if( bakup_api_url != NULL )
353 curl_rc = curl_easy_setopt(curl_handle2,
354 CURLOPT_WRITEFUNCTION,
355 evel_write_callback);
356 if (curl_rc != CURLE_OK)
358 rc = EVEL_CURL_LIBRARY_FAIL;
359 log_error_state("Failed to initialize libCURL with the API URL. "
360 "Error code=%d (%s)", curl_rc, curl_err_string2);
367 /***************************************************************************/
368 /* configure local ip address if provided */
369 /* Default ip if NULL */
370 /***************************************************************************/
371 if( source_ip != NULL )
373 snprintf(local_address,sizeof(local_address),source_ip);
374 if( local_address[0] != '\0' )
376 curl_rc = curl_easy_setopt(curl_handle,
379 if (curl_rc != CURLE_OK)
381 rc = EVEL_CURL_LIBRARY_FAIL;
382 log_error_state("Failed to initialize libCURL with the local address. "
383 "Error code=%d (%s)", curl_rc, curl_err_string);
388 if( source_ip_bakup != NULL )
390 snprintf(local_address,sizeof(local_address),source_ip_bakup);
391 if( local_address[0] != '\0' )
393 curl_rc = curl_easy_setopt(curl_handle2,
396 if (curl_rc != CURLE_OK)
398 rc = EVEL_CURL_LIBRARY_FAIL;
399 log_error_state("Failed to initialize bakup libCURL with the local address. "
400 "Error code=%d (%s)", curl_rc, curl_err_string2);
406 /***************************************************************************/
407 /* configure SSL options for HTTPS transfers */
408 /***************************************************************************/
411 if( cert_file_path != NULL )
413 curl_rc = curl_easy_setopt(curl_handle,
416 if (curl_rc != CURLE_OK)
418 rc = EVEL_CURL_LIBRARY_FAIL;
419 log_error_state("Failed to initialize libCURL with the client cert. "
420 "Error code=%d (%s)", curl_rc, curl_err_string);
423 if( bakup_api_url != NULL )
425 curl_rc = curl_easy_setopt(curl_handle2,
428 if (curl_rc != CURLE_OK)
430 rc = EVEL_CURL_LIBRARY_FAIL;
431 log_error_state("Failed to initialize libCURL with the client cert. "
432 "Error code=%d (%s)", curl_rc, curl_err_string);
438 if( key_file_path != NULL )
440 curl_rc = curl_easy_setopt(curl_handle,
443 if (curl_rc != CURLE_OK)
445 rc = EVEL_CURL_LIBRARY_FAIL;
446 log_error_state("Failed to initialize libCURL with the client key. "
447 "Error code=%d (%s)", curl_rc, curl_err_string);
450 if( bakup_api_url != NULL )
452 curl_rc = curl_easy_setopt(curl_handle2,
455 if (curl_rc != CURLE_OK)
457 rc = EVEL_CURL_LIBRARY_FAIL;
458 log_error_state("Failed to initialize libCURL with the client key. "
459 "Error code=%d (%s)", curl_rc, curl_err_string);
465 if( ca_info != NULL )
467 curl_rc = curl_easy_setopt(curl_handle,
470 if (curl_rc != CURLE_OK)
472 rc = EVEL_CURL_LIBRARY_FAIL;
473 log_error_state("Failed to initialize libCURL with the CA cert file. "
474 "Error code=%d (%s)", curl_rc, curl_err_string);
477 if( bakup_api_url != NULL )
479 curl_rc = curl_easy_setopt(curl_handle2,
482 if (curl_rc != CURLE_OK)
484 rc = EVEL_CURL_LIBRARY_FAIL;
485 log_error_state("Failed to initialize libCURL with the CA cert file. "
486 "Error code=%d (%s)", curl_rc, curl_err_string);
492 if( ca_file_path != NULL )
494 curl_rc = curl_easy_setopt(curl_handle,
497 if (curl_rc != CURLE_OK)
499 rc = EVEL_CURL_LIBRARY_FAIL;
500 log_error_state("Failed to initialize libCURL with the CA cert path. "
501 "Error code=%d (%s)", curl_rc, curl_err_string);
504 if( bakup_api_url != NULL )
506 curl_rc = curl_easy_setopt(curl_handle2,
509 if (curl_rc != CURLE_OK)
511 rc = EVEL_CURL_LIBRARY_FAIL;
512 log_error_state("Failed to initialize libCURL with the CA cert path. "
513 "Error code=%d (%s)", curl_rc, curl_err_string);
519 curl_rc = curl_easy_setopt(curl_handle,
520 CURLOPT_SSL_VERIFYPEER,
522 if (curl_rc != CURLE_OK)
524 rc = EVEL_CURL_LIBRARY_FAIL;
525 log_error_state("Failed to initialize libCURL with SSL Server verification. "
526 "Error code=%d (%s)", curl_rc, curl_err_string);
529 curl_rc = curl_easy_setopt(curl_handle,
530 CURLOPT_SSL_VERIFYHOST,
532 if (curl_rc != CURLE_OK)
534 rc = EVEL_CURL_LIBRARY_FAIL;
535 log_error_state("Failed to initialize libCURL with Client host verification. "
536 "Error code=%d (%s)", curl_rc, curl_err_string);
540 if( bakup_api_url != NULL )
542 curl_rc = curl_easy_setopt(curl_handle2,
543 CURLOPT_SSL_VERIFYPEER,
545 if (curl_rc != CURLE_OK)
547 rc = EVEL_CURL_LIBRARY_FAIL;
548 log_error_state("Failed to initialize libCURL with SSL Server verification. "
549 "Error code=%d (%s)", curl_rc, curl_err_string);
552 curl_rc = curl_easy_setopt(curl_handle2,
553 CURLOPT_SSL_VERIFYHOST,
555 if (curl_rc != CURLE_OK)
557 rc = EVEL_CURL_LIBRARY_FAIL;
558 log_error_state("Failed to initialize libCURL with Client host verification. "
559 "Error code=%d (%s)", curl_rc, curl_err_string);
567 /***************************************************************************/
568 /* some servers don't like requests that are made without a user-agent */
569 /* field, so we provide one. */
570 /***************************************************************************/
571 curl_rc = curl_easy_setopt(curl_handle,
573 "libcurl-agent/1.0");
574 if (curl_rc != CURLE_OK)
576 rc = EVEL_CURL_LIBRARY_FAIL;
577 log_error_state("Failed to initialize libCURL to upload. "
578 "Error code=%d (%s)", curl_rc, curl_err_string);
581 if( bakup_api_url != NULL )
583 curl_rc = curl_easy_setopt(curl_handle2,
585 "libcurl-agent/1.0");
586 if (curl_rc != CURLE_OK)
588 rc = EVEL_CURL_LIBRARY_FAIL;
589 log_error_state("Failed to initialize libCURL with the API URL. "
590 "Error code=%d (%s)", curl_rc, curl_err_string2);
596 /***************************************************************************/
597 /* Specify that we are going to POST data. */
598 /***************************************************************************/
599 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);
600 if (curl_rc != CURLE_OK)
602 rc = EVEL_CURL_LIBRARY_FAIL;
603 log_error_state("Failed to initialize libCURL to upload. "
604 "Error code=%d (%s)", curl_rc, curl_err_string);
607 if( bakup_api_url != NULL )
609 curl_rc = curl_easy_setopt(curl_handle2,CURLOPT_POST, 1L);
610 if (curl_rc != CURLE_OK)
612 rc = EVEL_CURL_LIBRARY_FAIL;
613 log_error_state("Failed to initialize libCURL with the API URL. "
614 "Error code=%d (%s)", curl_rc, curl_err_string2);
620 /***************************************************************************/
621 /* we want to use our own read function. */
622 /***************************************************************************/
623 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, read_callback);
624 if (curl_rc != CURLE_OK)
626 rc = EVEL_CURL_LIBRARY_FAIL;
627 log_error_state("Failed to initialize libCURL to upload using read "
628 "function. Error code=%d (%s)", curl_rc, curl_err_string);
631 if( bakup_api_url != NULL )
633 curl_rc = curl_easy_setopt(curl_handle2,CURLOPT_READFUNCTION, read_callback);
634 if (curl_rc != CURLE_OK)
636 rc = EVEL_CURL_LIBRARY_FAIL;
637 log_error_state("Failed to initialize libCURL with the API URL. "
638 "Error code=%d (%s)", curl_rc, curl_err_string2);
644 /***************************************************************************/
645 /* All of our events are JSON encoded. We also suppress the */
646 /* Expect: 100-continue header that we would otherwise get since it */
647 /* confuses some servers. */
649 /* @TODO: do AT&T want this behavior? */
650 /***************************************************************************/
651 hdr_chunk = curl_slist_append(hdr_chunk, "Content-type: application/json");
652 hdr_chunk = curl_slist_append(hdr_chunk, "Expect:");
654 /***************************************************************************/
655 /* set our custom set of headers. */
656 /***************************************************************************/
657 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, hdr_chunk);
658 if (curl_rc != CURLE_OK)
660 rc = EVEL_CURL_LIBRARY_FAIL;
661 log_error_state("Failed to initialize libCURL to use custom headers. "
662 "Error code=%d (%s)", curl_rc, curl_err_string);
665 if( bakup_api_url != NULL )
667 hdr_chunk2 = curl_slist_append(hdr_chunk2, "Content-type: application/json");
668 hdr_chunk2 = curl_slist_append(hdr_chunk2, "Expect:");
669 curl_rc = curl_easy_setopt(curl_handle2,CURLOPT_HTTPHEADER, hdr_chunk2);
670 if (curl_rc != CURLE_OK)
672 rc = EVEL_CURL_LIBRARY_FAIL;
673 log_error_state("Failed to initialize libCURL with the API URL. "
674 "Error code=%d (%s)", curl_rc, curl_err_string2);
681 /***************************************************************************/
682 /* Set the timeout for the operation. */
683 /***************************************************************************/
684 curl_rc = curl_easy_setopt(curl_handle,
687 if (curl_rc != CURLE_OK)
689 rc = EVEL_CURL_LIBRARY_FAIL;
690 log_error_state("Failed to initialize libCURL for API timeout. "
691 "Error code=%d (%s)", curl_rc, curl_err_string);
694 if( bakup_api_url != NULL )
696 curl_rc = curl_easy_setopt(curl_handle2,CURLOPT_TIMEOUT, EVEL_API_TIMEOUT);
697 if (curl_rc != CURLE_OK)
699 rc = EVEL_CURL_LIBRARY_FAIL;
700 log_error_state("Failed to initialize libCURL with the API URL. "
701 "Error code=%d (%s)", curl_rc, curl_err_string2);
707 /***************************************************************************/
708 /* Set that we want Basic authentication with username:password Base-64 */
709 /* encoded for the operation. */
710 /***************************************************************************/
711 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
712 if (curl_rc != CURLE_OK)
714 rc = EVEL_CURL_LIBRARY_FAIL;
715 log_error_state("Failed to initialize libCURL for Basic Authentication. "
716 "Error code=%d (%s)", curl_rc, curl_err_string);
719 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_USERNAME, username);
720 if (curl_rc != CURLE_OK)
722 rc = EVEL_CURL_LIBRARY_FAIL;
723 log_error_state("Failed to initialize libCURL with username. "
724 "Error code=%d (%s)", curl_rc, curl_err_string);
727 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_PASSWORD, password);
728 if (curl_rc != CURLE_OK)
730 rc = EVEL_CURL_LIBRARY_FAIL;
731 log_error_state("Failed to initialize libCURL with password. "
732 "Error code=%d (%s)", curl_rc, curl_err_string);
736 if( bakup_api_url != NULL )
738 curl_rc = curl_easy_setopt(curl_handle2, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
739 if (curl_rc != CURLE_OK)
741 rc = EVEL_CURL_LIBRARY_FAIL;
742 log_error_state("Failed to initialize libCURL for Basic Authentication. "
743 "Error code=%d (%s)", curl_rc, curl_err_string2);
746 curl_rc = curl_easy_setopt(curl_handle2, CURLOPT_USERNAME, username2);
747 if (curl_rc != CURLE_OK)
749 rc = EVEL_CURL_LIBRARY_FAIL;
750 log_error_state("Failed to initialize libCURL with username. "
751 "Error code=%d (%s)", curl_rc, curl_err_string2);
754 curl_rc = curl_easy_setopt(curl_handle2, CURLOPT_PASSWORD, password2);
755 if (curl_rc != CURLE_OK)
757 rc = EVEL_CURL_LIBRARY_FAIL;
758 log_error_state("Failed to initialize libCURL with password. "
759 "Error code=%d (%s)", curl_rc, curl_err_string2);
763 multi_handle = curl_multi_init();;
764 if (multi_handle == NULL)
766 rc = EVEL_CURL_LIBRARY_FAIL;
767 log_error_state("Failed to get libCURL Multi handle");
770 activmode = activitymode;
775 /***************************************************************************/
776 /* Initialize a message ring-buffer to be used between the foreground and */
777 /* the thread which sends the messages. This can't fail. */
778 /***************************************************************************/
779 if( ring_buf_size < EVEL_EVENT_BUFFER_DEPTH )
781 log_error_state("Warning: Failed to initialize Ring buffer size to %d. ",
785 ring_buffer_initialize(&event_buffer, EVEL_EVENT_BUFFER_DEPTH);
787 /***************************************************************************/
788 /* Initialize the priority post buffer to empty. */
789 /***************************************************************************/
790 priority_post.memory = NULL;
798 /**************************************************************************//**
799 * Run the event handler.
801 * Spawns the thread responsible for handling events and sending them to the
804 * @return Status code.
805 * @retval ::EVEL_SUCCESS if everything OK.
806 * @retval One of ::EVEL_ERR_CODES if there was a problem.
807 *****************************************************************************/
808 EVEL_ERR_CODES event_handler_run()
810 EVEL_ERR_CODES rc = EVEL_SUCCESS;
815 /***************************************************************************/
816 /* Start the event handler thread. */
817 /***************************************************************************/
818 evt_handler_state = EVT_HANDLER_INACTIVE;
819 if( curr_global_handles <= 1 )
820 pthread_rc = pthread_create(&evt_handler_thread, NULL, event_handler, NULL);
822 pthread_rc = pthread_create(&evt_handler_thread, NULL, event_multi_handler, NULL);
826 rc = EVEL_PTHREAD_LIBRARY_FAIL;
827 log_error_state("Failed to start event handler thread. "
828 "Error code=%d", pthread_rc);
835 /**************************************************************************//**
836 * Terminate the event handler.
838 * Shuts down the event handler thread in as clean a way as possible. Sets the
839 * global exit flag and then signals the thread to interrupt it since it's
840 * most likely waiting on the ring-buffer.
842 * Having achieved an orderly shutdown of the event handler thread, clean up
843 * the cURL library's resources cleanly.
845 * @return Status code.
846 * @retval ::EVEL_SUCCESS if everything OK.
847 * @retval One of ::EVEL_ERR_CODES if there was a problem.
848 *****************************************************************************/
849 EVEL_ERR_CODES event_handler_terminate()
851 EVEL_ERR_CODES rc = EVEL_SUCCESS;
854 EVENT_INTERNAL *event = NULL;
856 /***************************************************************************/
857 /* Make sure that we were initialized before trying to terminate the */
858 /* event handler thread. */
859 /***************************************************************************/
860 if (evt_handler_state != EVT_HANDLER_UNINITIALIZED)
862 /*************************************************************************/
863 /* Make sure that the event handler knows it's time to die. */
864 /*************************************************************************/
865 event = evel_new_internal_event(EVT_CMD_TERMINATE,"EVELinternal","EVELid");
868 /***********************************************************************/
869 /* We failed to get an event, but we don't bail out - we will just */
870 /* clean up what we can and continue on our way, since we're exiting */
872 /***********************************************************************/
873 EVEL_ERROR("Failed to get internal event - perform dirty exit instead!");
877 /***********************************************************************/
878 /* Post the event then wait for the Event Handler to exit. Set the */
879 /* global command, too, in case the ring-buffer is full. */
880 /***********************************************************************/
881 EVEL_DEBUG("Sending event to Event Hander to request it to exit.");
882 evt_handler_state = EVT_HANDLER_REQUEST_TERMINATE;
883 evel_post_event((EVENT_HEADER *) event);
884 pthread_join(evt_handler_thread, NULL);
885 EVEL_DEBUG("Event Handler thread has exited.");
890 EVEL_DEBUG("Event handler was not initialized, so no need to kill it");
893 /***************************************************************************/
894 /* Clean-up the cURL library. */
895 /***************************************************************************/
896 if (multi_handle != NULL)
898 curl_multi_cleanup(multi_handle);
900 if (curl_handle != NULL)
902 curl_easy_cleanup(curl_handle);
905 if (curl_handle2 != NULL)
907 curl_easy_cleanup(curl_handle2);
910 if (hdr_chunk != NULL)
912 curl_slist_free_all(hdr_chunk);
915 if (hdr_chunk2 != NULL)
917 curl_slist_free_all(hdr_chunk2);
922 /***************************************************************************/
923 /* Free off the stored API URL strings. */
924 /***************************************************************************/
925 if (evel_event_api_url != NULL)
927 free(evel_event_api_url);
928 evel_event_api_url = NULL;
930 if (evel_batch_api_url != NULL)
932 free(evel_batch_api_url);
933 evel_batch_api_url = NULL;
935 if (evel_throt_api_url != NULL)
937 free(evel_throt_api_url);
938 evel_throt_api_url = NULL;
945 /**************************************************************************//**
948 * @note So far as the caller is concerned, successfully posting the event
949 * relinquishes all responsibility for the event - the library will take care
950 * of freeing the event in due course.
952 * @param event The event to be posted.
954 * @returns Status code
955 * @retval EVEL_SUCCESS On success
956 * @retval "One of ::EVEL_ERR_CODES" On failure.
957 *****************************************************************************/
958 EVEL_ERR_CODES evel_post_event(EVENT_HEADER * event)
960 int rc = EVEL_SUCCESS;
964 /***************************************************************************/
965 /* Check preconditions. */
966 /***************************************************************************/
967 assert(event != NULL);
969 /***************************************************************************/
970 /* We need to make sure that we are either initializing or running */
971 /* normally before writing the event into the buffer so that we can */
972 /* guarantee that the ring-buffer empties properly on exit. */
973 /***************************************************************************/
974 if ((evt_handler_state == EVT_HANDLER_ACTIVE) ||
975 (evt_handler_state == EVT_HANDLER_INACTIVE) ||
976 (evt_handler_state == EVT_HANDLER_REQUEST_TERMINATE))
978 if (ring_buffer_write(&event_buffer, event) == 0)
980 log_error_state("Failed to write event to buffer - event dropped!");
981 rc = EVEL_EVENT_BUFFER_FULL;
982 evel_free_event(event);
987 /*************************************************************************/
988 /* System is not in active operation, so reject the event. */
989 /*************************************************************************/
990 log_error_state("Event Handler system not active - event dropped!");
991 rc = EVEL_EVENT_HANDLER_INACTIVE;
992 evel_free_event(event);
999 /**************************************************************************//**
1000 * Post an event to the Vendor Event Listener API.
1002 * @returns Status code
1003 * @retval EVEL_SUCCESS On success
1004 * @retval "One of ::EVEL_ERR_CODES" On failure.
1005 *****************************************************************************/
1006 static EVEL_ERR_CODES evel_post_api(char * msg, size_t size)
1008 int rc = EVEL_SUCCESS;
1009 CURLcode curl_rc = CURLE_OK;
1010 MEMORY_CHUNK rx_chunk;
1011 MEMORY_CHUNK tx_chunk;
1012 int http_response_code = 0;
1016 /***************************************************************************/
1017 /* Create the memory chunk to be used for the response to the post. The */
1018 /* will be realloced. */
1019 /***************************************************************************/
1020 rx_chunk.memory = malloc(1);
1021 assert(rx_chunk.memory != NULL);
1024 /***************************************************************************/
1025 /* Create the memory chunk to be sent as the body of the post. */
1026 /***************************************************************************/
1027 tx_chunk.memory = msg;
1028 tx_chunk.size = size;
1029 EVEL_DEBUG("Sending chunk of size %d", tx_chunk.size);
1031 /***************************************************************************/
1032 /* Point to the data to be received. */
1033 /***************************************************************************/
1034 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &rx_chunk);
1035 if (curl_rc != CURLE_OK)
1037 rc = EVEL_CURL_LIBRARY_FAIL;
1038 log_error_state("Failed to initialize libCURL to upload. "
1039 "Error code=%d (%s)", curl_rc, curl_err_string);
1042 EVEL_DEBUG("Initialized data to receive");
1044 /***************************************************************************/
1045 /* Pointer to pass to our read function */
1046 /***************************************************************************/
1047 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_READDATA, &tx_chunk);
1048 if (curl_rc != CURLE_OK)
1050 rc = EVEL_CURL_LIBRARY_FAIL;
1051 log_error_state("Failed to set upload data for libCURL to upload. "
1052 "Error code=%d (%s)", curl_rc, curl_err_string);
1055 EVEL_DEBUG("Initialized data to send");
1057 /***************************************************************************/
1058 /* Size of the data to transmit. */
1059 /***************************************************************************/
1060 curl_rc = curl_easy_setopt(curl_handle,
1061 CURLOPT_POSTFIELDSIZE,
1063 if (curl_rc != CURLE_OK)
1065 rc = EVEL_CURL_LIBRARY_FAIL;
1066 log_error_state("Failed to set length of upload data for libCURL to "
1067 "upload. Error code=%d (%s)", curl_rc, curl_err_string);
1070 EVEL_DEBUG("Initialized length of data to send");
1072 /***************************************************************************/
1073 /* Now run off and do what you've been told! */
1074 /***************************************************************************/
1075 curl_rc = curl_easy_perform(curl_handle);
1076 if (curl_rc != CURLE_OK)
1078 rc = EVEL_CURL_LIBRARY_FAIL;
1079 log_error_state("Failed to transfer an event to Vendor Event Listener! "
1080 "Error code=%d (%s)", curl_rc, curl_err_string);
1081 EVEL_ERROR("Dropped event: %s", msg);
1085 /***************************************************************************/
1086 /* See what response we got - any 2XX response is good. */
1087 /***************************************************************************/
1088 curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_response_code);
1089 EVEL_DEBUG("HTTP response code: %d", http_response_code);
1090 if ((http_response_code / 100) == 2)
1092 /*************************************************************************/
1093 /* If the server responded with data it may be interesting but not a */
1095 /*************************************************************************/
1096 if ((rx_chunk.size > 0) && (rx_chunk.memory != NULL))
1098 EVEL_DEBUG("Server returned data = %d (%s)",
1102 /***********************************************************************/
1103 /* If this is a response to priority post, then we're not interested. */
1104 /***********************************************************************/
1105 if (priority_post.memory != NULL)
1107 EVEL_ERROR("Ignoring priority post response");
1111 evel_handle_event_response(&rx_chunk, &priority_post);
1117 EVEL_ERROR("Unexpected HTTP response code: %d with data size %d (%s)",
1120 rx_chunk.size > 0 ? rx_chunk.memory : "NONE");
1121 EVEL_ERROR("Potentially dropped event: %s", msg);
1125 free(rx_chunk.memory);
1130 /**************************************************************************//**
1131 * Callback function to provide data to send.
1133 * Copy data into the supplied buffer, read_callback::ptr, checking size
1136 * @returns Number of bytes placed into read_callback::ptr. 0 for EOF.
1137 *****************************************************************************/
1138 static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
1141 size_t bytes_to_write = 0;
1142 MEMORY_CHUNK *tx_chunk = (MEMORY_CHUNK *)userp;
1146 bytes_to_write = min(size*nmemb, tx_chunk->size);
1148 if (bytes_to_write > 0)
1150 EVEL_DEBUG("Going to try to write %d bytes", bytes_to_write);
1151 strncpy((char *)ptr, tx_chunk->memory, bytes_to_write);
1152 tx_chunk->memory += bytes_to_write;
1153 tx_chunk->size -= bytes_to_write;
1154 rtn = bytes_to_write;
1158 EVEL_DEBUG("Reached EOF");
1165 /**************************************************************************//**
1166 * Callback function to provide returned data.
1168 * Copy data into the supplied buffer, write_callback::ptr, checking size
1171 * @returns Number of bytes placed into write_callback::ptr. 0 for EOF.
1172 *****************************************************************************/
1173 size_t evel_write_callback(void *contents,
1178 size_t realsize = size * nmemb;
1179 MEMORY_CHUNK * rx_chunk = (MEMORY_CHUNK *)userp;
1183 EVEL_DEBUG("Called with %d chunks of %d size = %d", nmemb, size, realsize);
1184 EVEL_DEBUG("rx chunk size is %d", rx_chunk->size);
1186 rx_chunk->memory = realloc(rx_chunk->memory, rx_chunk->size + realsize + 1);
1187 if(rx_chunk->memory == NULL) {
1188 /* out of memory! */
1189 printf("not enough memory (realloc returned NULL)\n");
1193 memcpy(&(rx_chunk->memory[rx_chunk->size]), contents, realsize);
1194 rx_chunk->size += realsize;
1195 rx_chunk->memory[rx_chunk->size] = 0;
1197 EVEL_DEBUG("Rx data: %s", rx_chunk->memory);
1198 EVEL_DEBUG("Returning: %d", realsize);
1205 /**************************************************************************//**
1206 * Post an event to the Vendor Event Listener API.
1208 * @returns Status code
1209 * @retval EVEL_SUCCESS On success
1210 * @retval "One of ::EVEL_ERR_CODES" On failure.
1211 *****************************************************************************/
1212 static EVEL_ERR_CODES evel_postmulti_message(char *msg, size_t size, int *still,
1213 CURL *handle, CURL *bhandle, int numhandles )
1215 int rc = EVEL_SUCCESS;
1216 CURLcode curl_rc = CURLE_OK;
1217 MEMORY_CHUNK rx_chunk[2];
1218 MEMORY_CHUNK tx_chunk[2];
1219 int http_response_code = 0, i;
1223 EVEL_INFO("Sending :%s: %d\n",msg, numhandles);
1225 /***************************************************************************/
1226 /* Create the memory chunk to be used for the response to the post. The */
1227 /* will be realloced. */
1228 /***************************************************************************/
1229 for (i=0;i<numhandles;i++)
1231 rx_chunk[i].memory = malloc(1);
1232 assert(rx_chunk[i].memory != NULL);
1233 rx_chunk[i].size = 0;
1235 /***************************************************************************/
1236 /* Create the memory chunk to be sent as the body of the post. */
1237 /***************************************************************************/
1238 tx_chunk[i].memory = msg;
1239 tx_chunk[i].size = size;
1240 EVEL_DEBUG("Sending chunk of size %d", tx_chunk[i].size);
1243 /***************************************************************************/
1244 /* Point to the data to be received. */
1245 /***************************************************************************/
1246 curl_rc = curl_easy_setopt(handle, CURLOPT_WRITEDATA, &rx_chunk[0]);
1247 if (curl_rc != CURLE_OK)
1249 rc = EVEL_CURL_LIBRARY_FAIL;
1250 log_error_state("Failed to initialize libCURL rx to upload. "
1251 "Error code=%d (%s)", curl_rc, curl_err_string);
1255 /***************************************************************************/
1256 /* Pointer to pass to our read function */
1257 /***************************************************************************/
1258 curl_rc = curl_easy_setopt(handle, CURLOPT_READDATA, &tx_chunk[0]);
1259 if (curl_rc != CURLE_OK)
1261 rc = EVEL_CURL_LIBRARY_FAIL;
1262 log_error_state("Failed to set upload data for libCURL tx to upload. "
1263 "Error code=%d (%s)", curl_rc, curl_err_string);
1266 /***************************************************************************/
1267 /* Size of the data to transmit. */
1268 /***************************************************************************/
1269 curl_rc = curl_easy_setopt(handle,
1270 CURLOPT_POSTFIELDSIZE,
1272 if (curl_rc != CURLE_OK)
1274 rc = EVEL_CURL_LIBRARY_FAIL;
1275 log_error_state("Failed to set length of upload data for libCURL to "
1276 "upload. Error code=%d (%s)", curl_rc, curl_err_string);
1283 /***************************************************************************/
1284 /* Point to the data to be received. */
1285 /***************************************************************************/
1286 curl_rc = curl_easy_setopt(bhandle, CURLOPT_WRITEDATA, &rx_chunk[1]);
1287 if (curl_rc != CURLE_OK)
1289 rc = EVEL_CURL_LIBRARY_FAIL;
1290 log_error_state("Failed to initialize libCURL2 rx to upload. "
1291 "Error code=%d (%s)", curl_rc, curl_err_string2);
1295 /***************************************************************************/
1296 /* Pointer to pass to our read function */
1297 /***************************************************************************/
1298 curl_rc = curl_easy_setopt(bhandle, CURLOPT_READDATA, &tx_chunk[1]);
1299 if (curl_rc != CURLE_OK)
1301 rc = EVEL_CURL_LIBRARY_FAIL;
1302 log_error_state("Failed to set upload data for libCURL2 tx to upload. "
1303 "Error code=%d (%s)", curl_rc, curl_err_string2);
1306 /***************************************************************************/
1307 /* Size of the data to transmit. */
1308 /***************************************************************************/
1309 curl_rc = curl_easy_setopt(bhandle,
1310 CURLOPT_POSTFIELDSIZE,
1312 if (curl_rc != CURLE_OK)
1314 rc = EVEL_CURL_LIBRARY_FAIL;
1315 log_error_state("Failed to set length of upload data for libCURL2 to "
1316 "upload. Error code=%d (%s)", curl_rc, curl_err_string);
1322 /***************************************************************************/
1323 /* Now run off and do what you've been told! */
1324 /***************************************************************************/
1325 curl_rc = curl_multi_perform(multi_handle, still);
1326 if (curl_rc != CURLE_OK)
1328 rc = EVEL_CURL_LIBRARY_FAIL;
1329 log_error_state("Failed to transfer an multi event to Vendor Event Listener! "
1330 "Error code=%d (%s)", curl_rc, curl_multi_strerror(curl_rc
1332 EVEL_ERROR("Dropped event: %s", msg);
1336 /***************************************************************************/
1337 /* See what response we got - any 2XX response is good. */
1338 /***************************************************************************/
1341 for (i=0;i<numhandles;i++)
1342 free(rx_chunk[i].memory);
1349 /**************************************************************************//**
1350 * Event Multi Post Handler.
1352 * Watch for messages coming on the internal queue and send them to the
1355 * param[in] jsonmsg json message to be sent.
1356 * param[in] size size of json message
1357 * param[in] currhandle Primary handle
1358 * param[in] url1 pimary url
1359 * param[in] bakkhandle Backup handle
1360 * param[in] url2 secondary url
1361 *****************************************************************************/
1362 static int evel_post_multiapi(char *jsonmsg, size_t size,CURL *currhandle, char *url1,
1363 CURL *bakkhandle, char *url2)
1365 int rc = EVEL_SUCCESS;
1366 CURLcode curl_rc = 0;
1368 int still_running,i;
1369 CURLMsg *msg; /* for picking up messages with the transfer status */
1370 int msgs_left; /* how many messages are left */
1372 /***************************************************************************/
1373 /* Set the URL for the API. */
1374 /***************************************************************************/
1375 curl_rc = curl_easy_setopt(currhandle, CURLOPT_URL, url1);
1376 if (curl_rc != CURLE_OK )
1378 rc = EVEL_CURL_LIBRARY_FAIL;
1379 log_error_state("Failed to initialize libCURL with the multi API URL. "
1380 "%s Error code=%d (%s)", url1, curl_rc, curl_err_string);
1383 if( url2 != NULL && activmode == 1 )
1385 curl_rc = curl_easy_setopt(bakkhandle, CURLOPT_URL, url2);
1386 if (curl_rc != CURLE_OK)
1388 rc = EVEL_CURL_LIBRARY_FAIL;
1389 log_error_state("Failed to initialize libCURL with the API URL. "
1390 "%s Error code=%d (%s)", url2, curl_rc, curl_err_string2);
1395 /* we start some action by calling perform right away */
1396 curl_multi_add_handle(multi_handle, currhandle);
1398 curl_multi_add_handle(multi_handle, bakkhandle);
1401 /* we start some action by calling perform right away */
1402 curl_multi_perform(multi_handle, &still_running);
1405 struct timeval timeout;
1406 int rc; /* select() return code */
1407 CURLMcode mc; /* curl_multi_fdset() return code */
1414 long curl_timeo = -1;
1420 /* set a suitable timeout to play around with */
1422 timeout.tv_usec = 0;
1424 curl_multi_timeout(multi_handle, &curl_timeo);
1425 if(curl_timeo >= 0) {
1426 timeout.tv_sec = curl_timeo / 1000;
1427 if(timeout.tv_sec > 1)
1430 timeout.tv_usec = (curl_timeo % 1000) * 1000;
1433 /* get file descriptors from the transfers */
1434 mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
1436 if(mc != CURLM_OK) {
1437 EVEL_ERROR("curl_multi_fdset() failed, code %d.\n", mc);
1441 /* On success the value of maxfd is guaranteed to be >= -1. We call
1442 select(maxfd + 1, ...); specially in case of (maxfd == -1) there are
1443 no fds ready yet so we call select(0, ...) --or Sleep() on Windows--
1444 to sleep 100ms, which is the minimum suggested value in the
1445 curl_multi_fdset() doc. */
1452 /* Portable sleep for platforms other than Windows. */
1453 struct timeval wait = { 0, 300000 }; /* 250ms */
1454 rc = select(0, NULL, NULL, NULL, &wait);
1458 /* Note that on some platforms 'timeout' may be modified by select().
1459 If you need access to the original value save a copy beforehand. */
1460 rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
1467 case 0: /* timeout */
1468 default: /* action */
1469 //curl_multi_perform(multi_handle, &still_running);
1470 evel_postmulti_message(jsonmsg, size, &still_running, currhandle, bakkhandle, nhandles);
1472 } while(still_running);
1474 /* See how the transfers went */
1475 while((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
1476 if(msg->msg == CURLMSG_DONE) {
1478 EVEL_DEBUG("Transfer status - %s\n", curl_multi_strerror(msg->data.result));
1480 /* Find out which handle this message is about */
1481 for(idx = 0; idx<nhandles; idx++) {
1482 if (msg->easy_handle == currhandle) break;
1483 else if(msg->easy_handle == bakkhandle) break;
1488 curl_rc = msg->data.result;
1491 curl_rc = msg->data.result;
1497 /* we start some action by calling perform right away */
1498 curl_multi_remove_handle(multi_handle, currhandle);
1500 curl_multi_remove_handle(multi_handle, bakkhandle);
1503 EVEL_DEBUG("Transfer completed with status %s\n", curl_multi_strerror(curl_rc));
1504 if( curl_rc == 0 || curl_rc == 55 )
1505 return EVEL_SUCCESS;
1507 return EVEL_CURL_LIBRARY_FAIL;
1510 /**************************************************************************//**
1513 * Watch for messages coming on the internal queue and send them to the
1516 * param[in] arg Argument - unused.
1517 *****************************************************************************/
1518 static void * event_multi_handler(void * arg __attribute__ ((unused)))
1521 EVENT_HEADER * msg = NULL;
1522 EVENT_INTERNAL * internal_msg = NULL;
1524 char json_body[EVEL_MAX_JSON_BODY];
1525 int rc = EVEL_SUCCESS;
1527 CURL *currhandler = NULL;
1528 CURL *bakhandler = NULL;
1529 char *send_url = NULL;
1530 char *send_url2 = NULL;
1532 EVEL_INFO("Event multi handler thread started");
1534 /***************************************************************************/
1535 /* Set this thread to be cancellable immediately. */
1536 /***************************************************************************/
1537 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type);
1539 /***************************************************************************/
1540 /* Set the handler as active, defending against weird situations like */
1541 /* immediately shutting down after initializing the library so the */
1542 /* handler never gets started up properly. */
1543 /***************************************************************************/
1544 if (evt_handler_state == EVT_HANDLER_INACTIVE)
1546 evt_handler_state = EVT_HANDLER_ACTIVE;
1550 EVEL_ERROR("Event Handler State was not INACTIVE at start-up - "
1551 "Handler will exit immediately!");
1554 currhandler = curl_handle;
1555 while (evt_handler_state == EVT_HANDLER_ACTIVE)
1557 /*************************************************************************/
1558 /* Wait for a message to be received. */
1559 /*************************************************************************/
1560 msg = ring_buffer_read(&event_buffer);
1562 /*************************************************************************/
1563 /* Internal events get special treatment while regular events get posted */
1564 /* to the far side. */
1565 /*************************************************************************/
1566 if (msg->event_domain == EVEL_DOMAIN_BATCH )
1568 EVEL_DEBUG("Batch event received");
1570 /***********************************************************************/
1571 /* Encode the event in JSON. */
1572 /***********************************************************************/
1573 json_size = evel_json_encode_batch_event(json_body, EVEL_MAX_JSON_BODY, msg);
1575 /***************************************************************************/
1576 /* Set the URL for the API. */
1577 /***************************************************************************/
1578 if( currhandler == curl_handle){
1579 send_url = evel_batch_api_url;
1580 send_url2 = evel_bbatch_api_url;
1581 bakhandler = curl_handle2;
1582 } else if(currhandler == curl_handle2) {
1583 send_url = evel_bbatch_api_url;
1584 send_url2 = evel_batch_api_url;
1585 bakhandler = curl_handle;
1588 /***********************************************************************/
1589 /* Send the JSON across the API. */
1590 /***********************************************************************/
1591 EVEL_DEBUG("Sending Batch JSON of size %d is: %s", json_size, json_body);
1592 rc = evel_post_multiapi(json_body, json_size, currhandler, send_url, bakhandler, send_url2);
1593 if (rc != EVEL_SUCCESS)
1595 EVEL_ERROR("Failed to transfer the data to %s. Error code=%d", send_url, rc);
1596 EVEL_INFO("Switched Collector ...");
1597 if( currhandler == curl_handle){
1598 currhandler = curl_handle2;
1599 bakhandler = curl_handle;
1600 } else if(currhandler == curl_handle2) {
1601 currhandler = curl_handle;
1602 bakhandler = curl_handle2;
1604 rc = evel_post_multiapi(json_body, json_size, currhandler, send_url2, bakhandler,send_url);
1605 if (rc != EVEL_SUCCESS)
1606 EVEL_ERROR("Failed to transfer the data to failover %s. Error code=%d", send_url2, rc);
1609 else if (msg->event_domain != EVEL_DOMAIN_INTERNAL )
1611 EVEL_DEBUG("External event received");
1613 /***********************************************************************/
1614 /* Encode the event in JSON. */
1615 /***********************************************************************/
1616 json_size = evel_json_encode_event(json_body, EVEL_MAX_JSON_BODY, msg);
1618 /***************************************************************************/
1619 /* Set the URL for the API. */
1620 /***************************************************************************/
1621 if( currhandler == curl_handle){
1622 send_url = evel_event_api_url;
1623 send_url2 = evel_bevent_api_url;
1624 bakhandler = curl_handle2;
1625 } else if(currhandler == curl_handle2) {
1626 send_url = evel_bevent_api_url;
1627 send_url2 = evel_event_api_url;
1628 bakhandler = curl_handle;
1631 /***********************************************************************/
1632 /* Send the JSON across the API. */
1633 /***********************************************************************/
1634 EVEL_DEBUG("Sending Batch JSON of size %d is: %s", json_size, json_body);
1635 rc = evel_post_multiapi(json_body, json_size, currhandler, send_url, bakhandler, send_url2);
1636 if (rc != EVEL_SUCCESS)
1638 EVEL_ERROR("Failed to transfer the data to %s. Error code=%d", send_url, rc);
1639 EVEL_INFO("Switched Collector ...");
1640 if( currhandler == curl_handle){
1641 currhandler = curl_handle2;
1642 bakhandler = curl_handle;
1643 } else if(currhandler == curl_handle2) {
1644 currhandler = curl_handle;
1645 bakhandler = curl_handle2;
1647 rc = evel_post_multiapi(json_body, json_size, currhandler, send_url2, bakhandler,send_url);
1648 if (rc != EVEL_SUCCESS)
1649 EVEL_ERROR("Failed to transfer the data to failover %s. Error code=%d", send_url2, rc);
1655 EVEL_DEBUG("Internal event received");
1656 internal_msg = (EVENT_INTERNAL *) msg;
1657 assert(internal_msg->command == EVT_CMD_TERMINATE);
1658 evt_handler_state = EVT_HANDLER_TERMINATING;
1661 /*************************************************************************/
1662 /* We are responsible for freeing the memory. */
1663 /*************************************************************************/
1664 evel_free_event(msg);
1667 /*************************************************************************/
1668 /* There may be a single priority post to be sent. */
1669 /*************************************************************************/
1670 if (priority_post.memory != NULL)
1672 EVEL_DEBUG("Priority Post");
1674 /***********************************************************************/
1675 /* Set the URL for the throttling API. */
1676 /***********************************************************************/
1677 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_throt_api_url);
1678 if (curl_rc != CURLE_OK)
1680 /*********************************************************************/
1681 /* This is only likely to happen with CURLE_OUT_OF_MEMORY, in which */
1682 /* case we carry on regardless. */
1683 /*********************************************************************/
1684 EVEL_ERROR("Failed to set throttling URL. Error code=%d", rc);
1688 rc = evel_post_api(priority_post.memory, priority_post.size);
1689 if (rc != EVEL_SUCCESS)
1691 EVEL_ERROR("Failed to transfer priority post. Error code=%d", rc);
1695 /***********************************************************************/
1696 /* Reinstate the URL for the event API. */
1697 /***********************************************************************/
1698 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_event_api_url);
1699 if (curl_rc != CURLE_OK)
1701 /*********************************************************************/
1702 /* This is only likely to happen with CURLE_OUT_OF_MEMORY, in which */
1703 /* case we carry on regardless. */
1704 /*********************************************************************/
1705 EVEL_ERROR("Failed to reinstate events URL. Error code=%d", rc);
1708 /***********************************************************************/
1709 /* We are responsible for freeing the memory. */
1710 /***********************************************************************/
1711 free(priority_post.memory);
1712 priority_post.memory = NULL;
1716 /***************************************************************************/
1717 /* The event handler is now exiting. The ring-buffer could contain events */
1718 /* which have not been processed, so deplete those. Because we've been */
1719 /* asked to exit we can be confident that the foreground will have stopped */
1720 /* sending events in so we know that this process will conclude! */
1721 /***************************************************************************/
1722 evt_handler_state = EVT_HANDLER_TERMINATING;
1723 while (!ring_buffer_is_empty(&event_buffer))
1725 EVEL_DEBUG("Reading event from buffer");
1726 msg = ring_buffer_read(&event_buffer);
1727 evel_free_event(msg);
1729 evt_handler_state = EVT_HANDLER_TERMINATED;
1730 EVEL_INFO("Event handler thread stopped");
1736 /**************************************************************************//**
1739 * Watch for messages coming on the internal queue and send them to the
1742 * param[in] arg Argument - unused.
1743 *****************************************************************************/
1744 static void * event_handler(void * arg __attribute__ ((unused)))
1747 EVENT_HEADER * msg = NULL;
1748 EVENT_INTERNAL * internal_msg = NULL;
1750 char json_body[EVEL_MAX_JSON_BODY];
1751 int rc = EVEL_SUCCESS;
1754 EVEL_INFO("Event handler thread started");
1756 /***************************************************************************/
1757 /* Set this thread to be cancellable immediately. */
1758 /***************************************************************************/
1759 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type);
1761 /***************************************************************************/
1762 /* Set the handler as active, defending against weird situations like */
1763 /* immediately shutting down after initializing the library so the */
1764 /* handler never gets started up properly. */
1765 /***************************************************************************/
1766 if (evt_handler_state == EVT_HANDLER_INACTIVE)
1768 evt_handler_state = EVT_HANDLER_ACTIVE;
1772 EVEL_ERROR("Event Handler State was not INACTIVE at start-up - "
1773 "Handler will exit immediately!");
1776 while (evt_handler_state == EVT_HANDLER_ACTIVE)
1778 /*************************************************************************/
1779 /* Wait for a message to be received. */
1780 /*************************************************************************/
1781 EVEL_DEBUG("Event handler getting any messages");
1782 msg = ring_buffer_read(&event_buffer);
1784 /*************************************************************************/
1785 /* Internal events get special treatment while regular events get posted */
1786 /* to the far side. */
1787 /*************************************************************************/
1788 if (msg->event_domain == EVEL_DOMAIN_BATCH )
1790 EVEL_DEBUG("Batch event received");
1792 /***********************************************************************/
1793 /* Encode the event in JSON. */
1794 /***********************************************************************/
1795 json_size = evel_json_encode_batch_event(json_body, EVEL_MAX_JSON_BODY, msg);
1797 /***************************************************************************/
1798 /* Set the URL for the API. */
1799 /***************************************************************************/
1800 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_batch_api_url);
1801 if (curl_rc != CURLE_OK)
1803 rc = EVEL_CURL_LIBRARY_FAIL;
1804 log_error_state("Failed to initialize libCURL with the Batch API URL. "
1805 "Error code=%d (%s)", curl_rc, curl_err_string);
1808 /***********************************************************************/
1809 /* Send the JSON across the API. */
1810 /***********************************************************************/
1811 EVEL_DEBUG("Sending Batch JSON of size %d is: %s", json_size, json_body);
1812 rc = evel_post_api(json_body, json_size);
1813 if (rc != EVEL_SUCCESS)
1815 EVEL_ERROR("Failed to transfer the data %s. Error code=%d", evel_batch_api_url, rc);
1818 else if (msg->event_domain != EVEL_DOMAIN_INTERNAL )
1820 EVEL_DEBUG("External event received");
1822 /***********************************************************************/
1823 /* Encode the event in JSON. */
1824 /***********************************************************************/
1825 json_size = evel_json_encode_event(json_body, EVEL_MAX_JSON_BODY, msg);
1827 /***************************************************************************/
1828 /* Set the URL for the API. */
1829 /***************************************************************************/
1830 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_event_api_url);
1831 if (curl_rc != CURLE_OK)
1833 rc = EVEL_CURL_LIBRARY_FAIL;
1834 log_error_state("Failed to initialize libCURL with the API URL. "
1835 "Error code=%d (%s)", curl_rc, curl_err_string);
1838 /***********************************************************************/
1839 /* Send the JSON across the API. */
1840 /***********************************************************************/
1841 EVEL_DEBUG("Sending JSON of size %d is: %s", json_size, json_body);
1842 rc = evel_post_api(json_body, json_size);
1843 if (rc != EVEL_SUCCESS)
1845 EVEL_ERROR("Failed to transfer the data %s. Error code=%d",evel_event_api_url, rc);
1850 EVEL_DEBUG("Internal event received");
1851 internal_msg = (EVENT_INTERNAL *) msg;
1852 assert(internal_msg->command == EVT_CMD_TERMINATE);
1853 evt_handler_state = EVT_HANDLER_TERMINATING;
1856 /*************************************************************************/
1857 /* We are responsible for freeing the memory. */
1858 /*************************************************************************/
1859 evel_free_event(msg);
1862 /*************************************************************************/
1863 /* There may be a single priority post to be sent. */
1864 /*************************************************************************/
1865 if (priority_post.memory != NULL)
1867 EVEL_DEBUG("Priority Post");
1869 /***********************************************************************/
1870 /* Set the URL for the throttling API. */
1871 /***********************************************************************/
1872 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_throt_api_url);
1873 if (curl_rc != CURLE_OK)
1875 /*********************************************************************/
1876 /* This is only likely to happen with CURLE_OUT_OF_MEMORY, in which */
1877 /* case we carry on regardless. */
1878 /*********************************************************************/
1879 EVEL_ERROR("Failed to set throttling URL. Error code=%d", rc);
1883 rc = evel_post_api(priority_post.memory, priority_post.size);
1884 if (rc != EVEL_SUCCESS)
1886 EVEL_ERROR("Failed to transfer priority post. Error code=%d", rc);
1890 /***********************************************************************/
1891 /* Reinstate the URL for the event API. */
1892 /***********************************************************************/
1893 curl_rc = curl_easy_setopt(curl_handle, CURLOPT_URL, evel_event_api_url);
1894 if (curl_rc != CURLE_OK)
1896 /*********************************************************************/
1897 /* This is only likely to happen with CURLE_OUT_OF_MEMORY, in which */
1898 /* case we carry on regardless. */
1899 /*********************************************************************/
1900 EVEL_ERROR("Failed to reinstate events URL. Error code=%d", rc);
1903 /***********************************************************************/
1904 /* We are responsible for freeing the memory. */
1905 /***********************************************************************/
1906 free(priority_post.memory);
1907 priority_post.memory = NULL;
1911 /***************************************************************************/
1912 /* The event handler is now exiting. The ring-buffer could contain events */
1913 /* which have not been processed, so deplete those. Because we've been */
1914 /* asked to exit we can be confident that the foreground will have stopped */
1915 /* sending events in so we know that this process will conclude! */
1916 /***************************************************************************/
1917 evt_handler_state = EVT_HANDLER_TERMINATING;
1918 while (!ring_buffer_is_empty(&event_buffer))
1920 EVEL_DEBUG("Reading event from buffer");
1921 msg = ring_buffer_read(&event_buffer);
1922 evel_free_event(msg);
1924 evt_handler_state = EVT_HANDLER_TERMINATED;
1925 EVEL_INFO("Event handler thread stopped");
1930 /**************************************************************************//**
1931 * Handle a JSON response from the listener, contained in a ::MEMORY_CHUNK.
1933 * Tokenize the response, and decode any tokens found.
1935 * @param chunk The memory chunk containing the response.
1936 * @param post The memory chunk in which to place any resulting POST.
1937 *****************************************************************************/
1938 void evel_handle_event_response(const MEMORY_CHUNK * const chunk,
1939 MEMORY_CHUNK * const post)
1941 jsmn_parser json_parser;
1942 jsmntok_t json_tokens[EVEL_MAX_RESPONSE_TOKENS];
1947 /***************************************************************************/
1948 /* Check preconditions. */
1949 /***************************************************************************/
1950 assert(chunk != NULL);
1951 assert(priority_post.memory == NULL);
1953 EVEL_DEBUG("Response size = %d", chunk->size);
1954 EVEL_DEBUG("Response = %s", chunk->memory);
1956 /***************************************************************************/
1957 /* Initialize the parser and tokenize the response. */
1958 /***************************************************************************/
1959 jsmn_init(&json_parser);
1960 num_tokens = jsmn_parse(&json_parser,
1964 EVEL_MAX_RESPONSE_TOKENS);
1968 EVEL_ERROR("Failed to parse JSON response. "
1969 "Error code=%d", num_tokens);
1971 else if (num_tokens == 0)
1973 EVEL_DEBUG("No tokens found in JSON response");
1977 //EVEL_DEBUG("Decode JSON response tokens");
1978 //if (!evel_handle_response_tokens(chunk, json_tokens, num_tokens, post))
1980 // EVEL_ERROR("Failed to handle JSON response.");
1987 /**************************************************************************//**
1988 * Handle a JSON response from the listener, as a list of tokens from JSMN.
1990 * @param chunk Memory chunk containing the JSON buffer.
1991 * @param json_tokens Array of tokens to handle.
1992 * @param num_tokens The number of tokens to handle.
1993 * @param post The memory chunk in which to place any resulting POST.
1994 * @return true if we handled the response, false otherwise.
1995 *****************************************************************************/
1996 bool evel_handle_response_tokens(const MEMORY_CHUNK * const chunk,
1997 const jsmntok_t * const json_tokens,
1998 const int num_tokens,
1999 MEMORY_CHUNK * const post)
2001 bool json_ok = false;
2005 /***************************************************************************/
2006 /* Check preconditions. */
2007 /***************************************************************************/
2008 assert(chunk != NULL);
2009 assert(json_tokens != NULL);
2010 assert(num_tokens < EVEL_MAX_RESPONSE_TOKENS);
2012 /***************************************************************************/
2013 /* Peek at the tokens to decide what the response it, then call the */
2014 /* appropriate handler to handle it. There is only one handler at this */
2016 /***************************************************************************/
2017 if (evel_tokens_match_command_list(chunk, json_tokens, num_tokens))
2019 json_ok = evel_handle_command_list(chunk, json_tokens, num_tokens, post);
2027 /**************************************************************************//**
2028 * Determine whether a list of tokens looks like a "commandList" response.
2030 * @param chunk Memory chunk containing the JSON buffer.
2031 * @param json_tokens Token to check.
2032 * @param num_tokens The number of tokens to handle.
2033 * @return true if the tokens look like a "commandList" match, or false.
2034 *****************************************************************************/
2035 bool evel_tokens_match_command_list(const MEMORY_CHUNK * const chunk,
2036 const jsmntok_t * const json_tokens,
2037 const int num_tokens)
2039 bool result = false;
2043 /***************************************************************************/
2044 /* Make some checks on the basic layout of the commandList. */
2045 /***************************************************************************/
2046 if ((num_tokens > 3) &&
2047 (json_tokens[0].type == JSMN_OBJECT) &&
2048 (json_tokens[1].type == JSMN_STRING) &&
2049 (json_tokens[2].type == JSMN_ARRAY) &&
2050 (evel_token_equals_string(chunk, &json_tokens[1], "commandList")))
2060 /**************************************************************************//**
2061 * Check that a string token matches a given input string.
2063 * @param chunk Memory chunk containing the JSON buffer.
2064 * @param json_token Token to check.
2065 * @param check_string String to check it against.
2066 * @return true if the strings match, or false.
2067 *****************************************************************************/
2068 bool evel_token_equals_string(const MEMORY_CHUNK * const chunk,
2069 const jsmntok_t * json_token,
2070 const char * check_string)
2072 bool result = false;
2076 const int token_length = json_token->end - json_token->start;
2077 const char * const token_string = chunk->memory + json_token->start;
2079 if (token_length == (int)strlen(check_string))
2081 result = (strncmp(token_string, check_string, token_length) == 0);