ha/service-mgmt/sm-common-1.0.0/src/sm_hw.c

824 lines
25 KiB
C

//
// Copyright (c) 2014 Wind River Systems, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
#include "sm_hw.h"
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <ifaddrs.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/gen_stats.h>
#include <linux/rtnetlink.h>
#include "sm_limits.h"
#include "sm_types.h"
#include "sm_debug.h"
#include "sm_selobj.h"
#include "sm_netlink.h"
typedef struct
{
bool inuse;
pthread_t thread_id;
uint32_t msg_seq;
int ioctl_socket;
int netlink_receive_socket;
SmHwCallbacksT callbacks;
} SmHwThreadInfoT;
static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
static SmHwThreadInfoT _threads[SM_THREADS_MAX];
// ****************************************************************************
// Hardware - Find Thread Info
// ===========================
static SmHwThreadInfoT* sm_hw_find_thread_info( void )
{
pthread_t thread_id = pthread_self();
unsigned int thread_i;
for( thread_i=0; SM_THREADS_MAX > thread_i; ++thread_i )
{
if( !(_threads[thread_i].inuse) )
continue;
if( thread_id == _threads[thread_i].thread_id )
return( &(_threads[thread_i]) );
}
return( NULL );
}
// ****************************************************************************
// ***************************************************************************
// Hardware - Get Interface By Network Address
// ===========================================
SmErrorT sm_hw_get_if_by_network_address( SmNetworkAddressT* network_address,
char if_name[] )
{
if_name[0] = '\0';
if(( SM_NETWORK_TYPE_IPV4 == network_address->type )||
( SM_NETWORK_TYPE_IPV4_UDP == network_address->type ))
{
struct ifaddrs *if_addr = NULL;
struct ifaddrs *if_addrs = NULL;
int result;
result = getifaddrs( &if_addrs );
if( 0 > result )
{
DPRINTFE( "Failed to get all interface addresses, error=%s.",
strerror( errno ) );
return( SM_FAILED );
}
for( if_addr=if_addrs; NULL != if_addr; if_addr=if_addr->ifa_next )
{
if( NULL == if_addr->ifa_addr )
continue;
if( AF_INET == if_addr->ifa_addr->sa_family )
{
struct sockaddr_in* ipv4_addr;
ipv4_addr = (struct sockaddr_in*) if_addr->ifa_addr;
if( ipv4_addr->sin_addr.s_addr
== network_address->u.ipv4.sin.s_addr )
{
snprintf( if_name, SM_INTERFACE_NAME_MAX_CHAR, "%s",
if_addr->ifa_name );
return( SM_OKAY );
}
}
}
} else if(( SM_NETWORK_TYPE_IPV6 == network_address->type )||
( SM_NETWORK_TYPE_IPV6_UDP == network_address->type ))
{
struct ifaddrs *if_addr = NULL;
struct ifaddrs *if_addrs = NULL;
int result;
result = getifaddrs( &if_addrs );
if( 0 > result )
{
DPRINTFE( "Failed to get all interface addresses, error=%s.",
strerror( errno ) );
return( SM_FAILED );
}
for( if_addr=if_addrs; NULL != if_addr; if_addr=if_addr->ifa_next )
{
if( NULL == if_addr->ifa_addr )
continue;
if( AF_INET6 == if_addr->ifa_addr->sa_family )
{
struct sockaddr_in6* ipv6_addr;
ipv6_addr = (struct sockaddr_in6*) if_addr->ifa_addr;
if( memcmp( &(ipv6_addr->sin6_addr),
&(network_address->u.ipv6.sin6),
sizeof(struct in6_addr) ))
{
snprintf( if_name, SM_INTERFACE_NAME_MAX_CHAR, "%s",
if_addr->ifa_name );
return( SM_OKAY );
}
}
}
} else {
DPRINTFE( "Unsupported network type (%s).",
sm_network_type_str(network_address->type) );
return( SM_FAILED );
}
return( SM_NOT_FOUND );
}
// ***************************************************************************
// ***************************************************************************
// Hardware - Get Interface Index
// ==============================
SmErrorT sm_hw_get_if_index( const char if_name[], int* if_index )
{
struct ifreq if_data;
SmHwThreadInfoT* thread_info;
*if_index = SM_INVALID_INDEX;
thread_info = sm_hw_find_thread_info();
if( NULL == thread_info )
{
DPRINTFE( "Failed to find thread information." );
return( SM_FAILED );
}
memset( &if_data, 0, sizeof(if_data) );
snprintf( if_data.ifr_name, sizeof(if_data.ifr_name), "%s", if_name );
if( 0 <= ioctl( thread_info->ioctl_socket, SIOCGIFINDEX, &if_data ) )
{
if( 0 != if_data.ifr_ifindex )
{
*if_index = if_data.ifr_ifindex;
} else {
DPRINTFD( "Interface index is invalid, if_name=%s.", if_name );
return( SM_FAILED );
}
} else {
DPRINTFD( "Failed to get interface index, if_name=%s, error=%s.",
if_name, strerror( errno ) );
return( SM_NOT_FOUND );
}
return( SM_OKAY );
}
// ***************************************************************************
// ***************************************************************************
// Hardware - Get Interface Name
// =============================
SmErrorT sm_hw_get_if_name( int if_index, char if_name[] )
{
struct ifreq if_data;
SmHwThreadInfoT* thread_info;
thread_info = sm_hw_find_thread_info();
if( NULL == thread_info )
{
DPRINTFE( "Failed to find thread information." );
return( SM_FAILED );
}
memset( &if_data, 0, sizeof(if_data) );
if_data.ifr_ifindex = if_index;
if( 0 <= ioctl( thread_info->ioctl_socket, SIOCGIFNAME, &if_data ) )
{
snprintf( if_name, SM_INTERFACE_NAME_MAX_CHAR, "%s", if_data.ifr_name );
} else {
DPRINTFD( "Failed to get interface name for interface index (%i), "
"error=%s.", if_index, strerror( errno ) );
return( SM_NOT_FOUND );
}
return( SM_OKAY );
}
// ***************************************************************************
// ***************************************************************************
// Sm Hardware - Get Interface State
// =================================
SmErrorT sm_hw_get_if_state( const char if_name[], bool* enabled )
{
struct ifreq if_data;
SmHwThreadInfoT* thread_info;
thread_info = sm_hw_find_thread_info();
if( NULL == thread_info )
{
DPRINTFE( "Failed to find thread information." );
return( SM_FAILED );
}
memset( &if_data, 0, sizeof(if_data) );
snprintf( if_data.ifr_name, sizeof(if_data.ifr_name), "%s", if_name );
if( 0 <= ioctl( thread_info->ioctl_socket, SIOCGIFFLAGS, &if_data ) )
{
if( if_data.ifr_flags & IFF_RUNNING )
{
*enabled = true;
} else {
*enabled = false;
}
} else {
DPRINTFE( "Failed to get interface state for interface (%s), "
"error=%s.", if_name, strerror( errno ) );
return( SM_NOT_FOUND );
}
return( SM_OKAY );
}
// ***************************************************************************
// ***************************************************************************
// Sm Hardware - Get All QDisc Asynchronous
// ========================================
SmErrorT sm_hw_get_all_qdisc_async( void )
{
struct nlmsghdr hdr;
struct tcmsg tc;
SmHwThreadInfoT* thread_info;
SmErrorT error;
thread_info = sm_hw_find_thread_info();
if( NULL == thread_info )
{
DPRINTFE( "Failed to find thread information." );
return( SM_FAILED );
}
memset( &hdr, 0, sizeof(hdr) );
hdr.nlmsg_len = NLMSG_LENGTH(sizeof(tc));
hdr.nlmsg_type = RTM_GETQDISC;
hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
hdr.nlmsg_seq = ++(thread_info->msg_seq);
hdr.nlmsg_pid = getpid();
memset( &tc, 0, sizeof(tc) );
tc.tcm_family = AF_UNSPEC;
error = sm_netlink_send_request( thread_info->netlink_receive_socket,
&hdr, &tc, sizeof(tc) );
if( SM_OKAY != error )
{
DPRINTFE( "Failed to request all qdisc info, error=%s.",
sm_error_str(error) );
return( error );
}
return( SM_OKAY );
}
// ***************************************************************************
// ****************************************************************************
// Hardware - Netlink Interface Message Dispatch
// =============================================
static void sm_hw_netlink_if_msg_dispatch( SmHwThreadInfoT* thread_info,
struct nlmsghdr* payload )
{
bool enabled;
struct ifinfomsg* if_msg;
int if_msg_size;
struct rtattr* if_msg_attr[IFLA_MAX+1];
struct rtattr* if_msg_attr_tmp = NULL;
SmHwInterfaceChangeDataT if_change_data;
SmErrorT error;
if_msg = (struct ifinfomsg*) NLMSG_DATA( payload );
if_msg_size = payload->nlmsg_len - NLMSG_LENGTH( sizeof(struct ifinfomsg) );
if( 0 >= if_msg_size )
{
DPRINTFE( "Netlink message has the wrong length( %i).", if_msg_size );
return;
}
if( AF_UNSPEC != if_msg->ifi_family )
{
DPRINTFE( "Unknown interface address family (%i) received.",
if_msg->ifi_family );
return;
}
memset( &if_change_data, 0, sizeof( if_change_data ) );
int attr_i;
for( attr_i=0; IFLA_MAX >= attr_i; ++attr_i )
{
if_msg_attr[attr_i] = NULL;
}
if_msg_attr_tmp = IFLA_RTA( if_msg );
while( RTA_OK( if_msg_attr_tmp, if_msg_size ) )
{
if( IFLA_MAX >= if_msg_attr_tmp->rta_type )
{
if_msg_attr[if_msg_attr_tmp->rta_type] = if_msg_attr_tmp;
}
if_msg_attr_tmp = RTA_NEXT( if_msg_attr_tmp, if_msg_size );
}
if( NULL == if_msg_attr[IFLA_IFNAME] )
{
DPRINTFE( "Missing interface name for interface index %i.",
if_msg->ifi_index );
return;
}
snprintf( if_change_data.interface_name,
sizeof(if_change_data.interface_name), "%s",
(char*) RTA_DATA( if_msg_attr[IFLA_IFNAME] ) );
error = sm_hw_get_if_state( if_change_data.interface_name, &enabled );
if(( SM_OKAY != error )&&( SM_NOT_FOUND != error ))
{
DPRINTFE( "Failed to get interface (%s) state information, "
"error=%s.", if_change_data.interface_name,
sm_error_str( error ) );
return;
} else if( SM_NOT_FOUND == error ) {
if_change_data.type = SM_HW_INTERFACE_CHANGE_TYPE_DELETE;
if_change_data.interface_state = SM_INTERFACE_STATE_UNKNOWN;
} else {
if_change_data.type = SM_HW_INTERFACE_CHANGE_TYPE_ADD;
if( enabled )
{
if_change_data.interface_state = SM_INTERFACE_STATE_ENABLED;
} else {
if_change_data.interface_state = SM_INTERFACE_STATE_DISABLED;
}
}
if( NULL != thread_info->callbacks.interface_change )
{
thread_info->callbacks.interface_change( &if_change_data );
}
}
// ****************************************************************************
// ****************************************************************************
// Hardware - Netlink IP Message Dispatch
// ======================================
static void sm_hw_netlink_ip_msg_dispatch( SmHwThreadInfoT* thread_info,
struct nlmsghdr* payload )
{
int len;
struct ifaddrmsg* if_addr_msg;
struct rtattr* rta;
SmHwIpChangeDataT ip_info;
SmErrorT error;
memset( &ip_info, 0, sizeof(SmHwIpChangeDataT) );
if( RTM_DELADDR == payload->nlmsg_type )
{
ip_info.type = SM_HW_IP_CHANGE_TYPE_DELETE;
} else {
ip_info.type = SM_HW_IP_CHANGE_TYPE_ADD;
}
if_addr_msg = (struct ifaddrmsg *) NLMSG_DATA( payload );
len = IFA_PAYLOAD( payload );
error = sm_hw_get_if_name( if_addr_msg->ifa_index,
ip_info.interface_name );
if( SM_OKAY != error )
{
DPRINTFE( "Failed to look up interface name, ifindex=%i, "
"error=%s.", (int) if_addr_msg->ifa_index,
sm_error_str(error) );
return;
}
DPRINTFD( "Interface %s (%i).", ip_info.interface_name,
if_addr_msg->ifa_index );
ip_info.prefix_len = if_addr_msg->ifa_prefixlen;
for( rta = (struct rtattr *) IFA_RTA( if_addr_msg );
RTA_OK( rta, len ); rta = RTA_NEXT( rta, len ) )
{
if(( IFA_ADDRESS == rta->rta_type )||( IFA_LOCAL == rta->rta_type )||
( IFA_BROADCAST == rta->rta_type || IFA_ANYCAST == rta->rta_type ))
{
char ip_str[SM_NETWORK_ADDRESS_MAX_CHAR];
if( IFA_ADDRESS == rta->rta_type )
{
ip_info.address_type = SM_HW_ADDRESS_TYPE_ADDRESS;
} else if( IFA_LOCAL == rta->rta_type ){
ip_info.address_type = SM_HW_ADDRESS_TYPE_LOCAL;
} else if( IFA_BROADCAST == rta->rta_type ){
ip_info.address_type = SM_HW_ADDRESS_TYPE_BROADCAST;
} else if( IFA_ANYCAST == rta->rta_type ){
ip_info.address_type = SM_HW_ADDRESS_TYPE_ANYCAST;
} else {
ip_info.address_type = SM_HW_ADDRESS_TYPE_UNKNOWN;
}
if( AF_INET == if_addr_msg->ifa_family )
{
ip_info.address.type = SM_NETWORK_TYPE_IPV4;
ip_info.address.u.ipv4.sin
= *((struct in_addr*) RTA_DATA( rta ));
sm_network_address_str( &(ip_info.address), ip_str );
DPRINTFD( "IPv4 address=%s/%i.", ip_str, ip_info.prefix_len );
if( NULL != thread_info->callbacks.ip_change )
{
thread_info->callbacks.ip_change( &ip_info );
}
} else if( AF_INET6 == if_addr_msg->ifa_family ) {
ip_info.address.type = SM_NETWORK_TYPE_IPV6;
memcpy( &(ip_info.address.u.ipv6.sin6),
(struct in6_addr*) RTA_DATA( rta ),
sizeof(struct in6_addr) );
sm_network_address_str( &(ip_info.address), ip_str );
DPRINTFD( "IPv6 address=%s/%i.", ip_str, ip_info.prefix_len );
if( NULL != thread_info->callbacks.ip_change )
{
thread_info->callbacks.ip_change( &ip_info );
}
}
}
}
}
// ****************************************************************************
// ****************************************************************************
// Hardware - Netlink QDisc Message Dispatch
// =========================================
static void sm_hw_netlink_qdisc_msg_dispatch( SmHwThreadInfoT* thread_info,
struct nlmsghdr* payload )
{
struct tcmsg* tc = (struct tcmsg*) NLMSG_DATA(payload);
struct rtattr* tb[TCA_MAX+1];
struct rtattr* rta;
int len;
SmHwQdiscInfoT qdisc_info;
SmErrorT error;
memset( &qdisc_info, 0, sizeof(qdisc_info) );
error = sm_hw_get_if_name( tc->tcm_ifindex, qdisc_info.interface_name );
if( SM_OKAY != error )
{
DPRINTFE( "Failed to look up interface name, ifindex=%i, error=%s.",
(int) tc->tcm_ifindex, sm_error_str(error) );
return;
}
len = payload->nlmsg_len - NLMSG_LENGTH(sizeof(struct tcmsg));
if( 0 > len )
{
DPRINTFE( "Invalid length detected." );
return;
}
memset( tb, 0, sizeof(tb) );
for( rta = TCA_RTA(tc); RTA_OK(rta, len); rta = RTA_NEXT(rta, len) )
{
if( TCA_MAX >= rta->rta_type )
{
tb[rta->rta_type] = rta;
}
}
if( NULL == tb[TCA_KIND] )
{
DPRINTFE( "Kind of qdisc not set." );
return;
}
snprintf( qdisc_info.queue_type, sizeof(qdisc_info.queue_type),
"%s", (const char *)RTA_DATA(tb[TCA_KIND]) );
snprintf( qdisc_info.handle, sizeof(qdisc_info.handle),
"%x:", tc->tcm_handle >> 16 );
if( tb[TCA_STATS2] )
{
struct rtattr* tb_stats[TCA_STATS_MAX+1];
memset( tb_stats, 0, sizeof(tb_stats) );
len = RTA_PAYLOAD(tb[TCA_STATS2]);
for( rta = (struct rtattr*) RTA_DATA(tb[TCA_STATS2]);
RTA_OK(rta, len); rta = RTA_NEXT(rta, len) )
{
if( TCA_STATS_MAX >= rta->rta_type )
{
tb_stats[rta->rta_type] = rta;
}
}
if( tb_stats[TCA_STATS_BASIC] )
{
struct gnet_stats_basic basic_stats;
memset( &basic_stats, 0, sizeof(basic_stats) );
if( RTA_PAYLOAD(tb_stats[TCA_STATS_BASIC]) > sizeof(basic_stats) )
{
memcpy( &basic_stats, RTA_DATA(tb_stats[TCA_STATS_BASIC]),
sizeof(basic_stats) );
} else {
memcpy( &basic_stats, RTA_DATA(tb_stats[TCA_STATS_BASIC]),
RTA_PAYLOAD(tb_stats[TCA_STATS_BASIC]) );
}
qdisc_info.bytes = basic_stats.bytes;
qdisc_info.packets = basic_stats.packets;
}
if( tb_stats[TCA_STATS_QUEUE] )
{
struct gnet_stats_queue queue_stats;
memset( &queue_stats, 0, sizeof(queue_stats) );
if( RTA_PAYLOAD(tb_stats[TCA_STATS_QUEUE]) > sizeof(queue_stats) )
{
memcpy( &queue_stats, RTA_DATA(tb_stats[TCA_STATS_QUEUE]),
sizeof(queue_stats) );
} else {
memcpy( &queue_stats, RTA_DATA(tb_stats[TCA_STATS_QUEUE]),
RTA_PAYLOAD(tb_stats[TCA_STATS_QUEUE]) );
}
qdisc_info.q_length = queue_stats.qlen;
qdisc_info.backlog = queue_stats.backlog;
qdisc_info.drops = queue_stats.drops;
qdisc_info.requeues = queue_stats.requeues;
qdisc_info.overlimits = queue_stats.overlimits;
}
}
if( NULL != thread_info->callbacks.qdisc_info )
{
thread_info->callbacks.qdisc_info( &qdisc_info );
}
}
// ****************************************************************************
// ****************************************************************************
// Hardware - Netlink Message Dispatch
// ===================================
static void sm_hw_netlink_msg_dispatch( int socket_fd,
struct sockaddr_nl* address, struct nlmsghdr* payload,
void* invocation_data )
{
SmHwThreadInfoT* thread_info = (SmHwThreadInfoT*) invocation_data;
switch( payload->nlmsg_type )
{
case RTM_NEWLINK:
case RTM_DELLINK:
case RTM_GETLINK:
sm_hw_netlink_if_msg_dispatch( thread_info, payload );
break;
case RTM_NEWADDR:
case RTM_GETADDR:
case RTM_DELADDR:
sm_hw_netlink_ip_msg_dispatch( thread_info, payload );
break;
case RTM_NEWQDISC:
case RTM_GETQDISC:
sm_hw_netlink_qdisc_msg_dispatch( thread_info, payload );
break;
case RTM_DELQDISC:
DPRINTFD( "Ignoring delete qdisc notification." );
break;
default:
DPRINTFI( "Ignoring netlink notification (%i).",
(int) payload->nlmsg_type );
break;
}
}
// ****************************************************************************
// ****************************************************************************
// Hardware - Netlink Dispatch
// ===========================
static void sm_hw_netlink_dispatch( int selobj, int64_t user_data )
{
SmHwThreadInfoT* thread_info;
SmErrorT error;
thread_info = sm_hw_find_thread_info();
if( NULL == thread_info )
{
DPRINTFE( "Failed to find thread information." );
return;
}
error = sm_netlink_receive( selobj, sm_hw_netlink_msg_dispatch,
thread_info );
if(( SM_OKAY != error )&&( SM_NO_MSG != error ))
{
DPRINTFE( "Failed to receive netlink message, error=%s.",
sm_error_str( error ) );
return;
}
}
// ***************************************************************************
// ***************************************************************************
// Hardware - Initialize
// =====================
SmErrorT sm_hw_initialize( SmHwCallbacksT* callbacks )
{
int flags;
SmHwThreadInfoT* thread_info = NULL;
SmErrorT error;
if( 0 != pthread_mutex_lock( &_mutex ) )
{
DPRINTFE( "Failed to capture mutex." );
return( SM_FAILED );
}
unsigned int thread_i;
for( thread_i=0; SM_THREADS_MAX > thread_i; ++thread_i )
{
if( !(_threads[thread_i].inuse) )
{
thread_info = &(_threads[thread_i]);
break;
}
}
if( NULL == thread_info )
{
DPRINTFE( "Failed to allocate thread information." );
goto ERROR;
}
memset( thread_info, 0, sizeof(SmHwThreadInfoT) );
thread_info->ioctl_socket = socket( PF_PACKET, SOCK_DGRAM, 0 );
if( 0 > thread_info->ioctl_socket )
{
DPRINTFE( "Unable to open a ioctl socket, errno=%s.",
strerror( errno ) );
goto ERROR;
}
flags = fcntl( thread_info->ioctl_socket, F_GETFL, 0 );
if( 0 > flags )
{
DPRINTFE( "Failed to get ioctl socket flags, error=%s.",
strerror( errno ) );
close( thread_info->ioctl_socket );
thread_info->ioctl_socket = -1;
goto ERROR;
}
if( 0 > fcntl( thread_info->ioctl_socket, F_SETFL, flags | O_NONBLOCK ) )
{
DPRINTFE( "Failed to set ioctl socket flags, error=%s.",
strerror( errno ) );
close( thread_info->ioctl_socket );
thread_info->ioctl_socket = -1;
goto ERROR;
}
if( NULL != callbacks )
{
error = sm_netlink_open( &(thread_info->netlink_receive_socket),
RTMGRP_LINK | RTMGRP_IPV4_IFADDR |
RTMGRP_IPV6_IFADDR | RTMGRP_TC );
if( SM_OKAY != error )
{
DPRINTFE( "Failed to open netlink receive socket, error=%s.",
sm_error_str( error ) );
close( thread_info->ioctl_socket );
thread_info->ioctl_socket = -1;
goto ERROR;
}
error = sm_selobj_register( thread_info->netlink_receive_socket,
sm_hw_netlink_dispatch, 0 );
if( SM_OKAY != error )
{
DPRINTFE( "Failed to register netlink receive selection object, "
"error=%s.", sm_error_str( error ) );
close( thread_info->ioctl_socket );
thread_info->ioctl_socket = -1;
sm_netlink_close( thread_info->netlink_receive_socket );
thread_info->netlink_receive_socket = -1;
goto ERROR;
}
memcpy( &(thread_info->callbacks), callbacks, sizeof(SmHwCallbacksT) );
}
thread_info->thread_id = pthread_self();
thread_info->inuse = true;
if( 0 != pthread_mutex_unlock( &_mutex ) )
{
DPRINTFE( "Failed to release mutex." );
return( SM_FAILED );
}
return( SM_OKAY );
ERROR:
if( 0 != pthread_mutex_unlock( &_mutex ) )
{
DPRINTFE( "Failed to release mutex." );
return( SM_FAILED );
}
return( SM_FAILED );
}
// ***************************************************************************
// ***************************************************************************
// Hardware - Finalize
// ===================
SmErrorT sm_hw_finalize( void )
{
SmHwThreadInfoT* thread_info;
SmErrorT error;
if( 0 != pthread_mutex_lock( &_mutex ) )
{
DPRINTFE( "Failed to capture mutex." );
return( SM_FAILED );
}
thread_info = sm_hw_find_thread_info();
if( NULL == thread_info )
{
DPRINTFE( "Failed to find thread information." );
goto DONE;
}
if( -1 < thread_info->netlink_receive_socket )
{
error = sm_selobj_deregister( thread_info->netlink_receive_socket );
if( SM_OKAY != error )
{
DPRINTFE( "Failed to deregister netlink receive selection object, "
"error=%s.", sm_error_str( error ) );
}
sm_netlink_close( thread_info->netlink_receive_socket );
thread_info->netlink_receive_socket = -1;
}
if( -1 < thread_info->ioctl_socket )
{
close( thread_info->ioctl_socket );
thread_info->ioctl_socket = -1;
}
DONE:
if( 0 != pthread_mutex_unlock( &_mutex ) )
{
DPRINTFE( "Failed to release mutex." );
}
return( SM_OKAY );
}
// ***************************************************************************