/* * Copyright (c) 2013, 2017 Wind River Systems, Inc. * * SPDX-License-Identifier: Apache-2.0 * */ /** * @file * Wind River CGTS Platform rmon HTTP Utilities. * */ #include #include #include #include /* for .. close and usleep */ #include /* for .. system */ #include /* for config dir reading */ #include /* for the list of conf file names */ #include /* for ... syslog */ #include /* for ... waitpid */ #include "rmon.h" #include "tokenUtil.h" /* for ... tokenUtil_get_ptr */ using namespace std; //#include "nodeClass.h" /* for ... maintenance class nodeLinkClass */ #include "rmonHttp.h" /* this module header */ //#include "rmonJsonUtil.h" /* Json Utilities */ #include "rmonApi.h" extern void rmonHdlr_remotelogging_handler ( struct evhttp_request *req, void *arg ); extern void rmonHdlr_ceilometer_handler( struct evhttp_request *req, void *arg ); void rmonHttpUtil_free_base ( libEvent_type & event ); static node_inv_type default_inv ; /***************************************************************************** * * Name : rmonHttpUtil_libEvent_init * * Purpose : Initialize the libEvent message for the HTTP request * *****************************************************************************/ int rmonHttpUtil_libEvent_init ( libEvent_type *ptr , string service, string ip, int port ) { ptr->type = EVHTTP_REQ_GET ; /* request type GET/PUT/PATCH etc */ /* Characteristics */ ptr->ip = ip ; ptr->port = port ; ptr->hostname = "default" ; /* Controls */ ptr->status = FAIL ; /* The handler must run to make this PASS */ ptr->active = false ; ptr->mutex = false ; ptr->stuck = 0 ; ptr->found = false ; ptr->count = 0 ; //ptr->stage = 0 ; ptr->result = "" ; ptr->timeout = 0 ; /* Personality */ ptr->service = service ; ptr->request = RMON_SERVICE_NONE ; /* Execution Data */ ptr->entity_path.clear() ; ptr->entity_path_next.clear() ; ptr->address.clear(); ptr->payload.clear(); ptr->response.clear(); ptr->user_agent.clear(); /* Better to access a default struct than a bad pointer */ ptr->inv_info_ptr = &default_inv ; /* Check for memory leaks */ if ( ptr->base ) { slog ("rmon http base memory leak avoidance (%p) fixme !!\n", ptr->base); event_base_free(ptr->base); } /* Create event base - like opening a socket */ ptr->base = event_base_new(); if ( ! ptr->base ) { elog ("Failed to create '%s' libEvent (event_base_new)\n", ptr->service.c_str()); return(FAIL_EVENT_BASE) ; } return (PASS); } void rmonHttpUtil_start_timer ( libEvent_type & event ) { clock_gettime (CLOCK_MONOTONIC, &event.start_ts ); } void rmonHttpUtil_stop_timer ( libEvent_type & event ) { clock_gettime (CLOCK_MONOTONIC, &event.stop_ts ); } /* *********************************************************************** * * Name : rmonHttpUtil_free_conn * * Description: Free an event's connection memory if it exists. * * ************************************************************************/ void rmonHttpUtil_free_conn ( libEvent_type & event ) { if ( event.conn ) { dlog ("rmond Free Connection (%p)\n", event.conn ); evhttp_connection_free ( event.conn ); event.conn = NULL ; } else { wlog ("rmond Already Freed Connection\n"); } } /* *********************************************************************** * * Name : rmonHttpUtil_free_base * * Description: Free an event's base memory if it exists. * * ************************************************************************/ void rmonHttpUtil_free_base ( libEvent_type & event ) { /* Free the base */ if ( event.base ) { dlog ("rmond Free Base (%p)\n", event.base ); event_base_free(event.base); event.base = NULL ; if ( event.conn ) { dlog ("rmond Free Connection (%p) --------- along with base\n", event.conn ); evhttp_connection_free ( event.conn ); event.conn = NULL ; } } else { wlog ("rmond Already Freed Event Base\n"); } } /***************************************************************************** * * Name : rmonHttpUtil_connect_new * * Purpose : generic HTTP Conect utility * *****************************************************************************/ int rmonHttpUtil_connect_new ( libEvent_type & event ) { if ( event.base ) { /* Open an http connection to specified IP and port */ event.conn = evhttp_connection_base_new ( event.base, NULL, event.ip.c_str(), event.port ); if ( event.conn ) { dlog("connect successfull \n"); return(PASS) ; } else { elog ("Failed to create http connection (evhttp_connection_base_new)\n"); return (FAIL_CONNECT); } } else { elog ("Null Event base\n"); return (FAIL_EVENT_BASE); } } /* generic HTTP Conect utility */ int rmonHttpUtil_request_new ( libEvent_type & event, void(*hdlr)(struct evhttp_request *, void *)) { int rc = PASS ; /* make a new request and bind the event handler to it */ event.req = evhttp_request_new( hdlr , event.base ); if ( ! event.req ) { dlog ("call to 'evhttp_request_new' returned NULL\n"); rc = FAIL ; } return (rc); } /* Fill in the output buffer */ /* return of 0 or -1 are errors */ int rmonHttpUtil_payload_add ( libEvent_type & event ) { int rc = PASS ; /* Returns the output buffer. */ event.buf = evhttp_request_get_output_buffer ( event.req ); /* Check for no buffer */ if ( ! event.buf ) { elog ("evhttp_request_get_output_buffer returned null (%p)\n", event.req ); rc = FAIL ; } else { /* write the payload into the buffer */ rc = evbuffer_add_printf ( event.buf, "%s", event.payload.c_str()); if ( rc == -1 ) { elog ("evbuffer_add_printf returned error (-1)\n"); rc = FAIL ; } else if ( rc == 0 ) { elog ("no data added to output buffer (len=0)\n"); rc = FAIL ; } else { rc = PASS ; } } return (rc); } /* get the output buffer length and convert it to a string that is returned */ string rmonHttpUtil_payload_len ( libEvent_type * ptr ) { string body_len ; char len_str[10] ; int len = evbuffer_get_length ( ptr->req->output_buffer ) ; if (( len == -1 ) || ( len == 0 )) { body_len = "" ; } else { memset ( &len_str[0], 0 , 10 ); sprintf ( &len_str[0], "%d", len ); body_len = len_str ; dlog3 ("%s Buffer Len:%s\n", ptr->hostname.c_str(), body_len.c_str() ); } return ( body_len ); } int rmonHttpUtil_header_add ( libEvent_type * ptr, http_headers_type * hdrs_ptr ) { int rc = PASS ; if ( hdrs_ptr->entries > MAX_HEADERS ) { elog ("%s Too many headers (%d:%d)\n", ptr->hostname.c_str(), MAX_HEADERS, hdrs_ptr->entries ); return FAIL ; } for ( int i = 0 ; i < hdrs_ptr->entries ; i++ ) { /* Add the header */ rc = evhttp_add_header( ptr->req->output_headers, hdrs_ptr->entry[i].key.c_str() , hdrs_ptr->entry[i].value.c_str()); if ( rc ) { elog ("evhttp_add_header returned failure (%d:%s:%s)\n", rc, hdrs_ptr->entry[i].key.c_str(), hdrs_ptr->entry[i].value.c_str()); rc = FAIL ; break ; } } return (rc); } /***************************************************************************** * * Name : rmonHttpUtil_request_make * * Purpose : Make the HTTP request * *****************************************************************************/ int rmonHttpUtil_request_make ( libEvent_type * ptr, enum evhttp_cmd_type type, string path ) { return (evhttp_make_request( ptr->conn, ptr->req, type, path.data())); } /***************************************************************************** * * Name : rmonHttpUtil_status * * Purpose : Get the status of the HTTP request * *****************************************************************************/ int rmonHttpUtil_status ( libEvent_type & event ) { int rc = PASS ; event.status = evhttp_request_get_response_code (event.req); switch (event.status) { case HTTP_OK: case 201: case 202: case 203: case 204: { dlog3 ("%s HTTP_OK (%d)\n", event.hostname.c_str(), event.status ); event.status = PASS ; break; } case 401: { /* Authentication error - refresh the token */ rc = RETRY ; break ; } case 0: { dlog ("%s Status: 0\n", event.hostname.c_str()); event.status = FAIL_HTTP_ZERO_STATUS ; rc = FAIL_HTTP_ZERO_STATUS ; break ; } default: { dlog ("%s Status: %d\n", event.hostname.c_str(), event.status ); rc = event.status ; break; } } return (rc); } /***************************************************************************** * * Name : rmonHttpUtil_api_request * * Purpose : Issue a HTTP REST API Request * *****************************************************************************/ #define URL_LEN 200 int rmonHttpUtil_api_request ( rmon_libEvent_enum request, libEvent_type & event, string command_path ) { http_headers_type hdrs ; enum evhttp_cmd_type type = EVHTTP_REQ_PUT ; int timeout = 1 ; int hdr_entry = 0 ; string payload = "" ; int rc = FAIL ; void(*handler)(struct evhttp_request *, void *) = NULL ; if ( request == REMOTE_LOGGING_REQUEST ) { /* Bind the handler for the request */ handler = &rmonHdlr_remotelogging_handler ; /* The type of HTTP request */ type = EVHTTP_REQ_GET ; /* set the timeout */ timeout = HTTP_REMOTELOGGING_TIMEOUT ; } else if ( request == CEILOMETER_SAMPLE_CREATE ) { /* Bind the handler for the request */ handler = &rmonHdlr_ceilometer_handler ; /* The type of HTTP request */ type = EVHTTP_REQ_POST ; /* set the timeout */ timeout = HTTP_CEILOMETER_TIMEOUT ; } else { slog ("%s Unsupported Request (%d)\n", event.hostname.c_str(), request); return (FAIL_BAD_CASE); } /* Establish connection */ if ( rmonHttpUtil_connect_new ( event )) { return (FAIL_CONNECT); } /* Create request */ if ( rmonHttpUtil_request_new ( event, handler )) { return (FAIL_REQUEST_NEW); } if ( type == EVHTTP_REQ_POST ) { /* Add payload to the output buffer but only for POST request. */ if ( rmonHttpUtil_payload_add ( event ) ) { event.status = FAIL_PAYLOAD_ADD; return ( event.status ); } if ( daemon_get_cfg_ptr()->debug_json ) { if ((!string_contains(event.payload,"token")) && (!string_contains(event.payload,"assword"))) { jlog ("%s Payload : %s\n", event.hostname.c_str(), event.payload.c_str() ); } else { jlog ("%s Payload : ... contains private content ...\n", event.hostname.c_str()); } } } if ( !command_path.empty() ) { event.address = command_path ; dlog (" Address : %s\n", event.address.c_str()); } /* Build the HTTP Header */ hdrs.entry[hdr_entry].key = "Host" ; hdrs.entry[hdr_entry].value = event.ip ; hdr_entry++; hdrs.entry[hdr_entry].key = "X-Auth-Project-Id" ; hdrs.entry[hdr_entry].value = "admin"; hdr_entry++; hdrs.entry[hdr_entry].key = "Content-Type" ; hdrs.entry[hdr_entry].value = "application/json" ; hdr_entry++; hdrs.entry[hdr_entry].key = "Accept" ; hdrs.entry[hdr_entry].value = "application/json" ; hdr_entry++; if ( request == CEILOMETER_SAMPLE_CREATE ) { hdrs.entry[hdr_entry].key = "User-Agent" ; hdrs.entry[hdr_entry].value = event.user_agent ; hdr_entry++; hdrs.entry[hdr_entry].key = "X-Auth-Token" ; hdrs.entry[hdr_entry].value = tokenUtil_get_ptr()->token ; hdr_entry++; } hdrs.entry[hdr_entry].key = "Connection" ; hdrs.entry[hdr_entry].value = "close" ; hdr_entry++; hdrs.entries = hdr_entry ; /* Add the headers */ if ( rmonHttpUtil_header_add ( &event, &hdrs )) { return (FAIL_HEADER_ADD); } rc = rmonHttpUtil_request_make ( &event, type, event.address.data() ); if ( rc == PASS ) { /* Send the message with timeout */ evhttp_connection_set_timeout(event.req->evcon, timeout); event_base_dispatch(event.base); rmonHttpUtil_free_conn ( event ); rmonHttpUtil_free_base ( event ); return(event.status) ; } elog ("%s Call to 'evhttp_make_request' failed (rc:%d)\n", event.hostname.c_str(), rc); return (FAIL_MAKE_REQUEST); } /***************************************************************************** * * Name : rmonHttpUtil_receive * * Purpose : Get the HTTP request response into a libEvent object * *****************************************************************************/ int rmonHttpUtil_receive ( libEvent_type & event ) { /* Send the request but don't wait for the response */ // int rc = event_base_loop(event.base, EVLOOP_NONBLOCK) ; int rc = event_base_loop(event.base, EVLOOP_ONCE) ; switch ( rc ) { case PASS: /* 0 */ { /* Set in-progress flag */ if ( event.active == false ) { /* look at the reported handler status */ if ( event.status != PASS ) rc = event.status ; rmonHttpUtil_log_event ( event ); } else { rc = RETRY ; } break ; } case 1: { dlog ("%s %s No Events Pending (1)\n", event.hostname.c_str(), event.service.c_str()); rc = FAIL ; break ; } case -1: { event.active = false ; elog ("%s %s Failed event_base_loop (-1)\n", event.hostname.c_str(), event.service.c_str()); rc = FAIL ; break ; } default: { event.active = false ; slog ("%s %s Failed event_base_loop - Unexpected Return (%d)\n", event.hostname.c_str(), event.service.c_str(), rc ); rc = FAIL ; break ; } } return (rc); } /* Get the length of the json response * Deal with oversized messages. * * Get the length of the buffer so we can * allocate one big enough to copy too. */ int rmonHttpUtil_get_length ( libEvent_type & event ) { event.response_len = evbuffer_get_length (event.req->input_buffer); if ( event.response_len == 0 ) { dlog ("%s %s Request Failed - Zero Length Response\n", event.hostname.c_str(), event.service.c_str()); event.status = FAIL_JSON_ZERO_LEN ; } else { event.status = PASS ; } return ( event.status ); } /* Load the response string into the event struct */ int rmonHttpUtil_get_response ( libEvent_type & event ) { if ( rmonHttpUtil_get_length ( event ) == PASS ) { size_t real_len ; /* Get a stack buffer, zero it, copy to it and terminate it */ char * stack_buf_ptr = (char*)malloc (event.response_len+1); memset ( stack_buf_ptr, 0, event.response_len+1 ); real_len = evbuffer_remove( event.req->input_buffer, stack_buf_ptr, event.response_len); if ( real_len != event.response_len ) { wlog ("%s %s Length differs from removed length (%ld:%ld)\n", event.hostname.c_str(), event.service.c_str(), event.response_len, real_len ); } /* Terminate the buffer , this is where the +1 above is required. * Without it there is memory corruption reported by Linux */ *(stack_buf_ptr+event.response_len) = '\0'; /* Store the response */ event.response = stack_buf_ptr ; dlog ("%s Response: %s\n", event.hostname.c_str(), event.response.c_str()); free (stack_buf_ptr); } return ( event.status ); } /***************************************************************************** * * Name : rmonHttpUtil_log_event * * Purpose : Log the HTTP event * *****************************************************************************/ void rmonHttpUtil_log_event ( libEvent_type & event ) { string event_sig = daemon_get_cfg_ptr()->debug_event ; dlog3 ("Event Signature (%s)\n", event_sig.c_str()); if ( !event_sig.compare(event.service) || (event.status)) { if ( !event.address.empty() ) { log_event ("%s %s Address : %s\n", event.hostname.c_str(), event_sig.c_str(), event.address.c_str()); } if (!event.payload.empty()) { if ((!string_contains(event.payload,"token")) && (!string_contains(event.payload,"assword"))) { log_event ("%s %s Payload : %s\n", event.hostname.c_str(), event_sig.c_str(), event.payload.c_str()); } else { log_event ("%s %s Payload : ... contains private content ...\n", event.hostname.c_str(), event_sig.c_str()); } } if ( !event.response.empty() ) { if ((!string_contains(event.payload,"token")) && (!string_contains(event.payload,"assword"))) { log_event ("%s %s Response: %s\n", event.hostname.c_str(), event_sig.c_str(), event.response.c_str()); } else { log_event ("%s %s Response: ... contains private content ...\n", event.hostname.c_str(), event_sig.c_str()); } } } }