// // Copyright (c) 2014,2023 Wind River Systems, Inc. // // SPDX-License-Identifier: Apache-2.0 // #include "sm_service_heartbeat_api.h" #include #include #include #include #include #include #include #include "sm_limits.h" #include "sm_types.h" #include "sm_debug.h" #include "sm_list.h" #include "sm_selobj.h" #include "sm_timer.h" #include "sm_util_types.h" typedef enum { SM_SERVICE_HEARTBEAT_MSG_TYPE_UNKNOWN, SM_SERVICE_HEARTBEAT_MSG_TYPE_START, SM_SERVICE_HEARTBEAT_MSG_TYPE_STOP, SM_SERVICE_HEARTBEAT_MSG_TYPE_OKAY, SM_SERVICE_HEARTBEAT_MSG_TYPE_WARN, SM_SERVICE_HEARTBEAT_MSG_TYPE_DEGRADE, SM_SERVICE_HEARTBEAT_MSG_TYPE_FAIL, SM_SERVICE_HEARTBEAT_MSG_TYPE_MAX, } SmServiceHeartbeatApiMsgTypeT; typedef struct { char service_name[SM_SERVICE_NAME_MAX_CHAR]; } SmServiceHeartbeatApiMsgStartT; typedef struct { char service_name[SM_SERVICE_NAME_MAX_CHAR]; } SmServiceHeartbeatApiMsgStopT; typedef struct { char service_name[SM_SERVICE_NAME_MAX_CHAR]; } SmServiceHeartbeatApiMsgOkayT; typedef struct { char service_name[SM_SERVICE_NAME_MAX_CHAR]; } SmServiceHeartbeatApiMsgWarnT; typedef struct { char service_name[SM_SERVICE_NAME_MAX_CHAR]; } SmServiceHeartbeatApiMsgDegradeT; typedef struct { char service_name[SM_SERVICE_NAME_MAX_CHAR]; } SmServiceHeartbeatApiMsgFailT; typedef struct { SmServiceHeartbeatApiMsgTypeT msg_type; union { SmServiceHeartbeatApiMsgStartT start; SmServiceHeartbeatApiMsgStopT stop; SmServiceHeartbeatApiMsgOkayT okay; SmServiceHeartbeatApiMsgWarnT warn; SmServiceHeartbeatApiMsgDegradeT degrade; SmServiceHeartbeatApiMsgFailT fail; } u; } SmServiceHeartbeatApiMsgT; static int _sm_heartbeat_api_server_fd = -1; static int _sm_heartbeat_api_client_fd = -1; static SmListT* _callbacks = NULL; static pthread_mutex_t heartbeat_callback_mutex; SmErrorT sm_service_heartbeat_api_mutex_initialize ( void ) { return sm_mutex_initialize(&heartbeat_callback_mutex, true); // PTHREAD_MUTEX_RECURSIVE } SmErrorT sm_service_heartbeat_api_mutex_finalize ( void ) { return sm_mutex_finalize(&heartbeat_callback_mutex); } // **************************************************************************** // Service Heartbeat API - Register Callbacks // ========================================== SmErrorT sm_service_heartbeat_api_register_callbacks( SmServiceHeartbeatCallbacksT* callbacks ) { if( 0 != pthread_mutex_lock( &heartbeat_callback_mutex ) ) { DPRINTFE( "Failed to capture mutex." ); return( SM_FAILED ); } SM_LIST_PREPEND( _callbacks, (SmListEntryDataPtrT) callbacks ); if( 0 != pthread_mutex_unlock( &heartbeat_callback_mutex ) ) { DPRINTFE( "Failed to release mutex." ); } return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Service Heartbeat API - Deregister Callbacks // ============================================ SmErrorT sm_service_heartbeat_api_deregister_callbacks( SmServiceHeartbeatCallbacksT* callbacks ) { if( 0 != pthread_mutex_lock( &heartbeat_callback_mutex ) ) { DPRINTFE( "Failed to capture mutex." ); return( SM_FAILED ); } SM_LIST_REMOVE( _callbacks, (SmListEntryDataPtrT) callbacks ); if( 0 != pthread_mutex_unlock( &heartbeat_callback_mutex ) ) { DPRINTFE( "Failed to release mutex." ); } return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Service Heartbeat API - Start Heartbeat // ======================================= SmErrorT sm_service_heartbeat_api_start_heartbeat( char service_name[] ) { SmServiceHeartbeatApiMsgT msg; SmServiceHeartbeatApiMsgStartT* start_msg = &(msg.u.start); memset( &msg, 0, sizeof(msg) ); msg.msg_type = SM_SERVICE_HEARTBEAT_MSG_TYPE_START; snprintf( start_msg->service_name, sizeof(start_msg->service_name), "%s", service_name ); if( 0 > send( _sm_heartbeat_api_server_fd, &msg, sizeof(msg), 0 ) ) { DPRINTFE( "Failed to signal service heartbeat for service (%s) to " "start, error=%s", service_name, strerror( errno ) ); return( SM_FAILED ); } DPRINTFD( "Heartbeat start called for service (%s).", service_name ); return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Service Heartbeat API - Stop Heartbeat // ====================================== SmErrorT sm_service_heartbeat_api_stop_heartbeat( char service_name[] ) { SmServiceHeartbeatApiMsgT msg; SmServiceHeartbeatApiMsgStopT* stop_msg = &(msg.u.stop); memset( &msg, 0, sizeof(msg) ); msg.msg_type = SM_SERVICE_HEARTBEAT_MSG_TYPE_STOP; snprintf( stop_msg->service_name, sizeof(stop_msg->service_name), "%s", service_name ); if( 0 > send( _sm_heartbeat_api_server_fd, &msg, sizeof(msg), 0 ) ) { DPRINTFE( "Failed to signal service heartbeat for service (%s) to " "stop, error=%s", service_name, strerror( errno ) ); return( SM_FAILED ); } DPRINTFD( "Heartbeat stop called for service (%s).", service_name ); return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Service Heartbeat API - Okay Heartbeat // ====================================== SmErrorT sm_service_heartbeat_api_okay_heartbeat( char service_name[] ) { SmServiceHeartbeatApiMsgT msg; SmServiceHeartbeatApiMsgOkayT* okay_msg = &(msg.u.okay); memset( &msg, 0, sizeof(msg) ); msg.msg_type = SM_SERVICE_HEARTBEAT_MSG_TYPE_OKAY; snprintf( okay_msg->service_name, sizeof(okay_msg->service_name), "%s", service_name ); if( 0 > send( _sm_heartbeat_api_client_fd, &msg, sizeof(msg), 0 ) ) { DPRINTFE( "Failed to send service heartbeat okay for service (%s), " "error=%s", service_name, strerror( errno ) ); return( SM_FAILED ); } return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Service Heartbeat API - Warn Heartbeat // ====================================== SmErrorT sm_service_heartbeat_api_warn_heartbeat( char service_name[] ) { SmServiceHeartbeatApiMsgT msg; SmServiceHeartbeatApiMsgWarnT* warn_msg = &(msg.u.warn); memset( &msg, 0, sizeof(msg) ); msg.msg_type = SM_SERVICE_HEARTBEAT_MSG_TYPE_WARN; snprintf( warn_msg->service_name, sizeof(warn_msg->service_name), "%s", service_name ); if( 0 > send( _sm_heartbeat_api_client_fd, &msg, sizeof(msg), 0 ) ) { DPRINTFE( "Failed to send service heartbeat warning for service (%s), " "error=%s", service_name, strerror( errno ) ); return( SM_FAILED ); } return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Service Heartbeat API - Degrade Heartbeat // ========================================= SmErrorT sm_service_heartbeat_api_degrade_heartbeat( char service_name[] ) { SmServiceHeartbeatApiMsgT msg; SmServiceHeartbeatApiMsgDegradeT* degrade_msg = &(msg.u.degrade); memset( &msg, 0, sizeof(msg) ); msg.msg_type = SM_SERVICE_HEARTBEAT_MSG_TYPE_DEGRADE; snprintf( degrade_msg->service_name, sizeof(degrade_msg->service_name), "%s", service_name ); if( 0 > send( _sm_heartbeat_api_client_fd, &msg, sizeof(msg), 0 ) ) { DPRINTFE( "Failed to send service heartbeat degrade for service (%s), " "error=%s", service_name, strerror( errno ) ); return( SM_FAILED ); } return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Service Heartbeat API - Fail Heartbeat // ====================================== SmErrorT sm_service_heartbeat_api_fail_heartbeat( char service_name[] ) { SmServiceHeartbeatApiMsgT msg; SmServiceHeartbeatApiMsgFailT* fail_msg = &(msg.u.fail); memset( &msg, 0, sizeof(msg) ); msg.msg_type = SM_SERVICE_HEARTBEAT_MSG_TYPE_FAIL; snprintf( fail_msg->service_name, sizeof(fail_msg->service_name), "%s", service_name ); if( 0 > send( _sm_heartbeat_api_client_fd, &msg, sizeof(msg), 0 ) ) { DPRINTFE( "Failed to send service heartbeat failure for service (%s), " "error=%s", service_name, strerror( errno ) ); return( SM_FAILED ); } return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Service Heartbeat API - Dispatch // ================================ static void sm_service_heartbeat_api_dispatch( int selobj, int64_t user_data ) { int bytes_read; SmListT* entry = NULL; SmListEntryDataPtrT entry_data; SmServiceHeartbeatCallbacksT* callbacks; SmServiceHeartbeatApiMsgT msg; int retry_count; for( retry_count = 5; retry_count != 0; --retry_count ) { bytes_read = recv( selobj, &msg, sizeof(msg), 0 ); if( 0 < bytes_read ) { break; } else if( 0 == bytes_read ) { // For connection oriented sockets, this indicates that the peer // has performed an orderly shutdown. return; } else if(( 0 > bytes_read )&&( EINTR != errno )) { DPRINTFE( "Failed to receive message, errno=%s.", strerror( errno ) ); return; } DPRINTFE( "Interrupted while receiving message, retry=%d, errno=%s.", retry_count, strerror( errno ) ); } if( bytes_read != sizeof(msg) ) { DPRINTFE( "Message truncated, bytes_read=%i, expected=%i", bytes_read, (int) sizeof(msg) ); return; } if( 0 != pthread_mutex_lock( &heartbeat_callback_mutex ) ) { DPRINTFE( "Failed to capture mutex." ); return; } SM_LIST_FOREACH( _callbacks, entry, entry_data ) { callbacks = (SmServiceHeartbeatCallbacksT*) entry_data; switch( msg.msg_type ) { case SM_SERVICE_HEARTBEAT_MSG_TYPE_START: if( NULL != callbacks->start_callback ) { callbacks->start_callback( msg.u.start.service_name ); } break; case SM_SERVICE_HEARTBEAT_MSG_TYPE_STOP: if( NULL != callbacks->stop_callback ) { callbacks->stop_callback( msg.u.stop.service_name ); } break; case SM_SERVICE_HEARTBEAT_MSG_TYPE_OKAY: if( NULL != callbacks->okay_callback ) { callbacks->okay_callback( msg.u.okay.service_name ); } break; case SM_SERVICE_HEARTBEAT_MSG_TYPE_WARN: if( NULL != callbacks->warn_callback ) { callbacks->warn_callback( msg.u.warn.service_name ); } break; case SM_SERVICE_HEARTBEAT_MSG_TYPE_DEGRADE: if( NULL != callbacks->degrade_callback ) { callbacks->degrade_callback( msg.u.degrade.service_name ); } break; case SM_SERVICE_HEARTBEAT_MSG_TYPE_FAIL: if( NULL != callbacks->fail_callback ) { callbacks->fail_callback( msg.u.fail.service_name ); } break; default: DPRINTFE( "Unknown message type (%i) received.", msg.msg_type ); break; } } if( 0 != pthread_mutex_unlock( &heartbeat_callback_mutex ) ) { DPRINTFE( "Failed to release mutex." ); } } // **************************************************************************** // **************************************************************************** // Service Heartbeat API - Initialize // ================================== SmErrorT sm_service_heartbeat_api_initialize( bool main_process ) { SmErrorT error; if( main_process ) { int flags; int sockets[2]; int result; result = socketpair( AF_UNIX, SOCK_DGRAM, 0, sockets ); if( 0 > result ) { DPRINTFE( "Failed to create communication service heartbeat " "sockets, error=%s.", strerror( errno ) ); return( SM_FAILED ); } int socket_i; for( socket_i=0; socket_i < 2; ++socket_i ) { flags = fcntl( sockets[socket_i], F_GETFL, 0 ); if( 0 > flags ) { DPRINTFE( "Failed to get service heartbeat socket (%i) flags, " "error=%s.", socket_i, strerror( errno ) ); return( SM_FAILED ); } result = fcntl( sockets[socket_i], F_SETFL, flags | O_NONBLOCK ); if( 0 > result ) { DPRINTFE( "Failed to set service heartbeat socket (%i) to " "non-blocking, error=%s.", socket_i, strerror( errno ) ); return( SM_FAILED ); } } _sm_heartbeat_api_server_fd = sockets[0]; _sm_heartbeat_api_client_fd = sockets[1]; error = sm_selobj_register( _sm_heartbeat_api_client_fd, sm_service_heartbeat_api_dispatch, 0 ); if( SM_OKAY != error ) { DPRINTFE( "Failed to register selection object, error=%s.", sm_error_str( error ) ); return( error ); } } else { error = sm_selobj_register( _sm_heartbeat_api_server_fd, sm_service_heartbeat_api_dispatch, 0 ); if( SM_OKAY != error ) { DPRINTFE( "Failed to register selection object, error=%s.", sm_error_str( error ) ); return( error ); } } return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Service Heartbeat API - Finialize // ================================= SmErrorT sm_service_heartbeat_api_finalize( bool main_process ) { SmErrorT error; if( main_process ) { if( -1 < _sm_heartbeat_api_client_fd ) { error = sm_selobj_deregister( _sm_heartbeat_api_client_fd ); if( SM_OKAY != error ) { DPRINTFE( "Failed to deregister selection object, error=%s.", sm_error_str( error ) ); } } } else { if( -1 < _sm_heartbeat_api_server_fd ) { error = sm_selobj_deregister( _sm_heartbeat_api_server_fd ); if( SM_OKAY != error ) { DPRINTFE( "Failed to deregister selection object, error=%s.", sm_error_str( error ) ); } } } return( SM_OKAY ); } // ****************************************************************************