ocs init
This commit is contained in:
454
plat/diameter/libfdcore/hooks.c
Normal file
454
plat/diameter/libfdcore/hooks.c
Normal file
@@ -0,0 +1,454 @@
|
||||
/*********************************************************************************************************
|
||||
* Software License Agreement (BSD License) *
|
||||
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
|
||||
* *
|
||||
* Copyright (c) 2013, WIDE Project and NICT *
|
||||
* All rights reserved. *
|
||||
* *
|
||||
* Redistribution and use of this software in source and binary forms, with or without modification, are *
|
||||
* permitted provided that the following conditions are met: *
|
||||
* *
|
||||
* * Redistributions of source code must retain the above *
|
||||
* copyright notice, this list of conditions and the *
|
||||
* following disclaimer. *
|
||||
* *
|
||||
* * Redistributions in binary form must reproduce the above *
|
||||
* copyright notice, this list of conditions and the *
|
||||
* following disclaimer in the documentation and/or other *
|
||||
* materials provided with the distribution. *
|
||||
* *
|
||||
* * Neither the name of the WIDE Project or NICT nor the *
|
||||
* names of its contributors may be used to endorse or *
|
||||
* promote products derived from this software without *
|
||||
* specific prior written permission of WIDE Project and *
|
||||
* NICT. *
|
||||
* *
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
|
||||
*********************************************************************************************************/
|
||||
|
||||
#include "fdcore-internal.h"
|
||||
|
||||
/* Structures for the fd_hook_data_hdl management */
|
||||
static struct fd_hook_data_hdl {
|
||||
size_t pmd_size;
|
||||
void (*pmd_init_cb)(struct fd_hook_permsgdata *);
|
||||
void (*pmd_fini_cb)(struct fd_hook_permsgdata *);
|
||||
} HDH_array[FD_HOOK_HANDLE_LIMIT];
|
||||
static int max_index = 0;
|
||||
static pthread_mutex_t HDH_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* The structure linked from the msg structure list */
|
||||
struct pmd_list_item {
|
||||
struct fd_list chain; /* this list is ordered by hdl */
|
||||
struct fd_hook_data_hdl * hdl;
|
||||
struct fd_hook_permsgdata { } pmd; /* this data belongs to the extension; we only know the size of it */
|
||||
};
|
||||
|
||||
#define sizeof_pmd(hdl) (((size_t)&((struct pmd_list_item *)0)->pmd) + hdl->pmd_size)
|
||||
|
||||
/* Now a hook registered by an extension */
|
||||
struct fd_hook_hdl {
|
||||
struct fd_list chain[HOOK_LAST+1];
|
||||
void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, void * regdata);
|
||||
void *regdata;
|
||||
struct fd_hook_data_hdl *data_hdl;
|
||||
};
|
||||
|
||||
/* Array of those hooks */
|
||||
struct {
|
||||
struct fd_list sentinel;
|
||||
pthread_rwlock_t rwlock;
|
||||
} HS_array[HOOK_LAST+1];
|
||||
|
||||
/* Initialize the array of sentinels for the hooks */
|
||||
int fd_hooks_init(void)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i <= HOOK_LAST; i++) {
|
||||
fd_list_init(&HS_array[i].sentinel, NULL);
|
||||
CHECK_POSIX( pthread_rwlock_init(&HS_array[i].rwlock, NULL) );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get a slot in the array */
|
||||
int fd_hook_data_register(
|
||||
size_t permsgdata_size,
|
||||
void (*permsgdata_init_cb) (struct fd_hook_permsgdata *),
|
||||
void (*permsgdata_fini_cb) (struct fd_hook_permsgdata *),
|
||||
struct fd_hook_data_hdl **new_handle)
|
||||
{
|
||||
int ret = ENOSPC, idx;
|
||||
TRACE_ENTRY("%zd %p %p %p", permsgdata_size, permsgdata_init_cb, permsgdata_fini_cb, new_handle);
|
||||
|
||||
CHECK_PARAMS( permsgdata_size && new_handle );
|
||||
|
||||
CHECK_POSIX( pthread_mutex_lock(&HDH_lock) );
|
||||
if (max_index < FD_HOOK_HANDLE_LIMIT) {
|
||||
idx = max_index++;
|
||||
ret = 0;
|
||||
}
|
||||
CHECK_POSIX( pthread_mutex_unlock(&HDH_lock) );
|
||||
|
||||
if (ret == 0) {
|
||||
HDH_array[idx].pmd_size = permsgdata_size;
|
||||
HDH_array[idx].pmd_init_cb = permsgdata_init_cb;
|
||||
HDH_array[idx].pmd_fini_cb = permsgdata_fini_cb;
|
||||
*new_handle = &HDH_array[idx];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register a new hook callback */
|
||||
int fd_hook_register ( uint32_t type_mask,
|
||||
void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, void * regdata),
|
||||
void *regdata,
|
||||
struct fd_hook_data_hdl *data_hdl,
|
||||
struct fd_hook_hdl ** handler )
|
||||
{
|
||||
struct fd_hook_hdl * newhdl = NULL;
|
||||
int i;
|
||||
|
||||
TRACE_ENTRY("%x %p %p %p %p", type_mask, fd_hook_cb, regdata, data_hdl, handler);
|
||||
|
||||
CHECK_PARAMS( fd_hook_cb && handler );
|
||||
|
||||
CHECK_MALLOC( newhdl = malloc(sizeof(struct fd_hook_hdl)) );
|
||||
memset(newhdl, 0, sizeof(struct fd_hook_hdl));
|
||||
|
||||
newhdl->fd_hook_cb = fd_hook_cb;
|
||||
newhdl->regdata = regdata;
|
||||
newhdl->data_hdl = data_hdl;
|
||||
|
||||
for (i=0; i <= HOOK_LAST; i++) {
|
||||
fd_list_init(&newhdl->chain[i], newhdl);
|
||||
if (type_mask & (1<<i)) {
|
||||
CHECK_POSIX( pthread_rwlock_wrlock(&HS_array[i].rwlock) );
|
||||
fd_list_insert_before( &HS_array[i].sentinel, &newhdl->chain[i]);
|
||||
CHECK_POSIX( pthread_rwlock_unlock(&HS_array[i].rwlock) );
|
||||
}
|
||||
}
|
||||
|
||||
*handler = newhdl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* free this hook callback */
|
||||
int fd_hook_unregister( struct fd_hook_hdl * handler )
|
||||
{
|
||||
int i;
|
||||
TRACE_ENTRY("%p", handler);
|
||||
CHECK_PARAMS( handler );
|
||||
|
||||
for (i=0; i <= HOOK_LAST; i++) {
|
||||
if ( ! FD_IS_LIST_EMPTY(&handler->chain[i])) {
|
||||
CHECK_POSIX( pthread_rwlock_wrlock(&HS_array[i].rwlock) );
|
||||
fd_list_unlink(&handler->chain[i]);
|
||||
CHECK_POSIX( pthread_rwlock_unlock(&HS_array[i].rwlock) );
|
||||
}
|
||||
}
|
||||
|
||||
free(handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* callback for the libfdproto to free the data associated with a message */
|
||||
static void pmdl_free(struct fd_msg_pmdl *pmdl)
|
||||
{
|
||||
/* destroy all the items in the list */
|
||||
while (!FD_IS_LIST_EMPTY(&pmdl->sentinel)) {
|
||||
struct pmd_list_item * li = (struct pmd_list_item *)(pmdl->sentinel.next);
|
||||
if (li->hdl->pmd_fini_cb) {
|
||||
(*li->hdl->pmd_fini_cb)(&li->pmd);
|
||||
}
|
||||
fd_list_unlink(&li->chain);
|
||||
free(li);
|
||||
}
|
||||
CHECK_POSIX_DO( pthread_mutex_destroy(&pmdl->lock), );
|
||||
pmdl->sentinel.o = NULL;
|
||||
}
|
||||
|
||||
/* Save the list of pmd into the message structure, as well as the callback to free this list */
|
||||
void fd_hook_associate(struct msg * msg, struct fd_msg_pmdl * pmdl)
|
||||
{
|
||||
struct fd_msg_pmdl * in_msg;
|
||||
|
||||
CHECK_PARAMS_DO( msg && pmdl, return );
|
||||
in_msg = fd_msg_pmdl_get(msg);
|
||||
ASSERT(in_msg && (in_msg->sentinel.o == NULL)); /* error / already initialized ??? */
|
||||
in_msg->sentinel.o = pmdl_free;
|
||||
/* Now move all items from the pmdl pointer into the initialized list */
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
|
||||
fd_list_move_end(&in_msg->sentinel, &pmdl->sentinel);
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
|
||||
pmdl_free(pmdl);
|
||||
/* We're done */
|
||||
}
|
||||
|
||||
/* Return the location of the permsgdata area corresponding to this handle, after eventually having created it. Return NULL in case of failure */
|
||||
static struct fd_hook_permsgdata * get_or_create_pmd(struct fd_msg_pmdl *pmdl, struct fd_hook_hdl * h)
|
||||
{
|
||||
struct fd_hook_permsgdata * ret = NULL;
|
||||
struct fd_list * li;
|
||||
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
|
||||
|
||||
if (pmdl->sentinel.o == NULL) {
|
||||
pmdl->sentinel.o = pmdl_free;
|
||||
}
|
||||
|
||||
/* Search in the list for an item with the same handle. The list is ordered by this handle */
|
||||
for (li=pmdl->sentinel.next; li != &pmdl->sentinel; li = li->next) {
|
||||
struct pmd_list_item * pli = (struct pmd_list_item *) li;
|
||||
if (pli->hdl == h->data_hdl)
|
||||
ret = &pli->pmd;
|
||||
if (pli->hdl >= h->data_hdl)
|
||||
break;
|
||||
}
|
||||
if (!ret) {
|
||||
/* we need to create a new one and insert before li */
|
||||
struct pmd_list_item * pli;
|
||||
CHECK_MALLOC_DO( pli = malloc(sizeof_pmd(h->data_hdl)), );
|
||||
if (pli) {
|
||||
memset(pli, 0, sizeof_pmd(h->data_hdl));
|
||||
fd_list_init(&pli->chain, pli);
|
||||
pli->hdl = h->data_hdl;
|
||||
ret = &pli->pmd;
|
||||
if (h->data_hdl->pmd_init_cb) {
|
||||
(*h->data_hdl->pmd_init_cb)(ret);
|
||||
}
|
||||
fd_list_insert_before(li, &pli->chain);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct fd_hook_permsgdata * fd_hook_get_request_pmd(struct fd_hook_data_hdl *data_hdl, struct msg * answer)
|
||||
{
|
||||
struct msg * qry;
|
||||
struct fd_msg_pmdl *pmdl;
|
||||
struct fd_hook_permsgdata * ret = NULL;
|
||||
struct fd_list * li;
|
||||
|
||||
CHECK_FCT_DO( fd_msg_answ_getq(answer, &qry), return NULL );
|
||||
if (!qry)
|
||||
return NULL;
|
||||
|
||||
pmdl = fd_msg_pmdl_get(qry);
|
||||
if (!pmdl)
|
||||
return NULL;
|
||||
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
|
||||
/* Search in the list for an item with the same handle. The list is ordered by this handle */
|
||||
for (li=pmdl->sentinel.next; li != &pmdl->sentinel; li = li->next) {
|
||||
struct pmd_list_item * pli = (struct pmd_list_item *) li;
|
||||
if (pli->hdl == data_hdl)
|
||||
ret = &pli->pmd;
|
||||
if (pli->hdl >= data_hdl)
|
||||
break;
|
||||
}
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Create a mask */
|
||||
uint32_t fd_hook_mask_helper(int dummy, ...)
|
||||
{
|
||||
va_list ap;
|
||||
uint32_t ret = 0;
|
||||
int next;
|
||||
|
||||
va_start(ap, dummy);
|
||||
while ((next = va_arg(ap, int)) >= 0) {
|
||||
if (next > HOOK_LAST)
|
||||
break; /* invalid parameter */
|
||||
ret |= (1<<next);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static pthread_mutex_t hook_default_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
static char * hook_default_buf = NULL;
|
||||
static size_t hook_default_len = 0;
|
||||
|
||||
/* The function that does the work of calling the extension's callbacks and also managing the permessagedata structures */
|
||||
void fd_hook_call(enum fd_hook_type type, struct msg * msg, struct fd_peer * peer, void * other, struct fd_msg_pmdl * pmdl)
|
||||
{
|
||||
struct fd_list * li;
|
||||
ASSERT(type <= HOOK_LAST);
|
||||
int call_default = 0;
|
||||
|
||||
/* lock the list of hooks for this type */
|
||||
CHECK_POSIX_DO( pthread_rwlock_rdlock(&HS_array[type].rwlock), );
|
||||
|
||||
pthread_cleanup_push( fd_cleanup_rwlock, &HS_array[type].rwlock );
|
||||
|
||||
if (FD_IS_LIST_EMPTY(&HS_array[type].sentinel)) {
|
||||
call_default = 1;
|
||||
} else {
|
||||
/* for each registered hook */
|
||||
for (li = HS_array[type].sentinel.next; li != &HS_array[type].sentinel; li = li->next) {
|
||||
struct fd_hook_hdl * h = (struct fd_hook_hdl *)li->o;
|
||||
struct fd_hook_permsgdata * pmd = NULL;
|
||||
|
||||
/* do we need to handle pmd ? */
|
||||
if (h->data_hdl && pmdl) {
|
||||
pmd = get_or_create_pmd(pmdl, h);
|
||||
}
|
||||
|
||||
/* Now, call this callback */
|
||||
(*h->fd_hook_cb)(type, msg, &peer->p_hdr, other, pmd, h->regdata);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_cleanup_pop(0);
|
||||
|
||||
/* done */
|
||||
CHECK_POSIX_DO( pthread_rwlock_unlock(&HS_array[type].rwlock), );
|
||||
|
||||
if (call_default) {
|
||||
CHECK_POSIX_DO( pthread_mutex_lock(&hook_default_mtx), );
|
||||
|
||||
pthread_cleanup_push( fd_cleanup_mutex, &hook_default_mtx );
|
||||
|
||||
/* There was no registered handler, default behavior for this hook */
|
||||
switch (type) {
|
||||
case HOOK_DATA_RECEIVED: {
|
||||
struct fd_cnx_rcvdata *rcv_data = other;
|
||||
LOG_A("RCV: %zd bytes", rcv_data->length);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_RECEIVED: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_D("RCV from '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_LOCAL: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_full(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_A("Handled to framework for sending: %s", hook_default_buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_SENDING: {
|
||||
LOG_A("SENDING message to '%s'", peer ? peer->p_hdr.info.pi_diamid : "<unknown>");
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_SENT: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_D("SENT to '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_FAILOVER: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_D("Failing over message sent to '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_PARSING_ERROR: {
|
||||
if (msg) {
|
||||
DiamId_t id = NULL;
|
||||
if (!fd_msg_source_get( msg, &id, NULL ))
|
||||
id = (DiamId_t)"<error getting source>";
|
||||
|
||||
if (!id)
|
||||
id = (DiamId_t)"<local>";
|
||||
|
||||
CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
|
||||
LOG_E("Parsing error: '%s' for the following message received from '%s':", (char *)other, (char *)id);
|
||||
LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
|
||||
} else {
|
||||
struct fd_cnx_rcvdata *rcv_data = other;
|
||||
CHECK_MALLOC_DO(fd_dump_extend_hexdump(&hook_default_buf, &hook_default_len, NULL, rcv_data->buffer, rcv_data->length, 0, 0), break);
|
||||
LOG_E("Parsing error: cannot parse %zdB buffer from '%s': %s", rcv_data->length, peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_PARSING_ERROR2: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
|
||||
LOG_E("Returning following message after parsing error:");
|
||||
LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_ROUTING_ERROR: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_E("Routing error: '%s' for the following message:", (char *)other);
|
||||
LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_ROUTING_FORWARD: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_D("FORWARDING: %s", hook_default_buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_ROUTING_LOCAL: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_D("DISPATCHING: %s", hook_default_buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_MESSAGE_DROPPED: {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_E("Message discarded ('%s'):", (char *)other);
|
||||
LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_PEER_CONNECT_FAILED: {
|
||||
if (msg) {
|
||||
CHECK_MALLOC_DO(fd_msg_dump_full(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
LOG_N("Connection to '%s' failed: '%s'; CER/CEA dump:", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", (char *)other);
|
||||
LOG_SPLIT(FD_LOG_NOTICE, " ", hook_default_buf, NULL);
|
||||
} else {
|
||||
LOG_D("Connection to '%s' failed: %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", (char *)other);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case HOOK_PEER_CONNECT_SUCCESS: {
|
||||
DiamId_t id = NULL;
|
||||
if ((!fd_msg_source_get( msg, &id, NULL )) && (id == NULL)) { /* The CEA is locally issued */
|
||||
fd_msg_answ_getq(msg, &msg); /* We dump the CER in that case */
|
||||
}
|
||||
CHECK_MALLOC_DO(fd_msg_dump_full(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
|
||||
char protobuf[40];
|
||||
if (peer) {
|
||||
CHECK_FCT_DO(fd_peer_cnx_proto_info(&peer->p_hdr, protobuf, sizeof(protobuf)), break );
|
||||
} else {
|
||||
protobuf[0] = '-';
|
||||
protobuf[1] = '\0';
|
||||
}
|
||||
LOG_N("Connected to '%s' (%s), remote capabilities: ", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", protobuf);
|
||||
LOG_SPLIT(FD_LOG_NOTICE, " ", hook_default_buf, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pthread_cleanup_pop(0);
|
||||
|
||||
CHECK_POSIX_DO( pthread_mutex_unlock(&hook_default_mtx), );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user