Mercurial > hg > waaad
view waaad/session.c @ 415:540ed390c04f
Added sess_destroy function
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Tue, 16 Jun 2009 13:37:46 +0900 |
parents | 860f41038ea2 |
children |
line wrap: on
line source
/********************************************************************************************************* * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@nict.go.jp> * * * * Copyright (c) 2009, 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. * *********************************************************************************************************/ /* Session module. * * See session.h and session-api.h for more information on the functions and types involved. */ #include "waaad-internal.h" /* More details about the session implementation: * * The sess_id_t, representing a session id, are stored as _sess_id_t objects in a hash table. * These objects contains generic information about the session: sid string, creation time, ... * They keep a refcount. when the refcount becomes 0, the object is freed. * * The sess_reg_t represent clients for the module. * The data is stored in a list which root is this _sess_reg_t object. * Each element of the list contains a pointer to a _sess_id_t, a pointer to user's data, and * a reference to a cleanup handler. */ /* Types and globals definitions */ /* The following two integers are used to generate values which are eternaly unique */ static uint32_t g_sid_h; /* initialized to the current time at module initialization */ static uint32_t g_sid_l; /* incremented each time a session id is created. */ static pthread_mutex_t g_sid_lock = PTHREAD_MUTEX_INITIALIZER; /************ sess_id_t ***********/ /* Size of the hash table to hold the sess_id_t objects (pow of 2. ex: 6 => 2^6 = 64). must be between 0 and 31. */ #define SESS_HASH_SIZE 6 /* The sess_id_t internal definition */ typedef struct { uti_list_t chain; /* Chaining information. the "o" field is the pointer to the session id string, \0 terminated */ uint32_t hash; /* The value of the hash of the sid string */ int eyec; /* An eye catcher to ensure the object is valid */ int rc; /* The refcount */ struct timespec ts; /* Time of the object creation */ } _sess_id_t; /* The eyec value */ #define _SI_EYEC 0x53551D /* some useful cast macros */ #define _SI( _si ) ((_sess_id_t *)(_si)) #define _SID( _si ) ((char *)( _LIST( _si )->o)) /* Macro to check an element is valid */ #define VALIDATE_SI( _si ) ( ((_si) != NULL) && ( _SI(_si)->eyec == _SI_EYEC) ) /* The hash table */ static struct { uti_list_t sentinel; /* sentinel element for this sublist */ pthread_mutex_t lock; /* the mutex for this sublist */ int count; /* Number of items in this list */ } sess_hash [ 1 << SESS_HASH_SIZE ] ; #define H_MASK( __hash ) ((__hash) & (( 1 << SESS_HASH_SIZE ) - 1)) #define H_LIST( _hash ) (&(sess_hash[H_MASK(_hash)].sentinel)) #define H_LOCK( _hash ) (&(sess_hash[H_MASK(_hash)].lock )) #define H_COUNT(_hash ) ( sess_hash[H_MASK(_hash)].count ) /************ sess_reg_t and data ***********/ /* A list element */ typedef struct { uti_list_t list; /* Elements are ordered in the same order as the hash table: by hash and string */ /* the "o" field is a pointer to the session element (_sess_id_t) this list element refers */ void (*clncb)(void *);/* The cleanup callback to call on data when the element is destroyed */ void *data; /* The data that is registered for this client and session */ } _sd_data_t; #define _SD( _sd_ ) ( (_sd_data_t *)( _sd_) ) #define _SDI( _sd_ ) ( _SI( _LIST( _sd_ )->o ) ) /* Now the sess_reg_t internal definition */ typedef struct { uti_list_t list; /* List of the registered clients with no particular order; only to ensure proper cleanup in the end. */ int eyec; /* An eye catcher to ensure the object is valid */ pthread_mutex_t lock; /* A lock to protect the list of data */ uti_list_t sentinel;/* Sentinel for the list of _sd_data_t of this client */ } _sess_reg_t; /* The sentinel for the list of registered handlers */ static uti_list_t _srt_sentinel; static pthread_mutex_t _str_lock; /* lock to protect the list of registered clients */ /* The eyec value */ #define _SR_EYEC 0x5355DA1A /* useful cast macro */ #define _SR( _sr ) ((_sess_reg_t *)(_sr)) /* Macro to check an element is valid */ #define VALIDATE_SR( _sr ) ( ((_sr) != NULL) && ( _SR(_sr)->eyec == _SR_EYEC) ) /********************************************************************************************************/ /* Function to compare a (hash, sid, len) with a sess_id_element. */ static __inline__ int cmp_si(uint32_t hash1, char * sid1, size_t sid1len, _sess_id_t * si2) { if (hash1 < si2->hash) return -1; if (hash1 > si2->hash) return 1; return strncmp(sid1, _SID(si2), sid1len); /* si2's sid is NULL terminated */ } /* Function to find a location for a (hash, sid) in a _sess_id_t list. * return 0 if the element is found, -1 if the previous element has been returned (in *loc) * The sublist lock must be held */ static __inline__ int find_si(uint32_t hash, char * sid, size_t sidlen, uti_list_t * sentinel, uti_list_t ** loc ) { int cmp = -1; for ((*loc) = sentinel; (*loc)->next != sentinel; (*loc) = (*loc)->next) { cmp = cmp_si(hash, sid, sidlen, _SI((*loc)->next)); if (!cmp) (*loc) = (*loc)->next; /* we found the sid in the list */ if (cmp <= 0) /* The next element in the list is bigger than our element, so stop here */ break; } return cmp; } /* Function to find a location for a _sess_id_t in a _sd_data_t list (_sess_reg_t -> sentinel). * return 0 if the element is found, -1 if the previous element has been returned (in *loc) * The list lock must be held */ static __inline__ int find_sd(_sess_id_t * sid, uti_list_t * sentinel, uti_list_t ** loc ) { int cmp = -1; size_t len = strlen(_SID(sid)); for ((*loc) = sentinel; (*loc)->next != sentinel; (*loc) = (*loc)->next) { cmp = cmp_si(sid->hash, _SID(sid), len, _SDI((*loc)->next)); if (!cmp) (*loc) = (*loc)->next; /* we found the sid in the list */ if (cmp <= 0) /* The next element in the list is bigger than our element, so stop here */ break; } return cmp; } /********************************************************************************************************/ /* Remove a piece of data linked from a _sess_reg_t. The _sess_reg_t lock must be held */ static int _destroy_sr_data(_sd_data_t * sd, void ** data) { /* Call the destroy function for this element */ if (data) *data = sd->data; else if (sd->clncb) (*sd->clncb)(sd->data); /* Unlink the session */ CHECK_FCT( sess_unlink((sess_id_t **)&_LIST(sd)->o) ); /* Unchain this element */ uti_list_unlink( _LIST(sd) ); /* And free it */ free(sd); return 0; } /* Remove any data linked from a _sess_reg_t. The _sess_reg_t lock must be held */ static int _destroy_sr_alldata(uti_list_t * sentinel) { while (!IS_LIST_EMPTY( sentinel )) { CHECK_FCT( _destroy_sr_data(_SD(sentinel->next), NULL) ); } return 0; } /* Remove a _sess_reg_t and any data linked from it. The _str_lock lock must be held */ static int _destroy_sr(_sess_reg_t * item) { int ret = 0; /* Lock the object */ CHECK_POSIX( pthread_mutex_lock( &item->lock ) ); /* Invalidate */ item->eyec = 0xdead; /* Destroy any data contained in this object */ ret = _destroy_sr_alldata(&item->sentinel); if (ret != 0) { TRACE_DEBUG(INFO, "_destroy_sr_alldata failed: %s", strerror(ret)); CHECK_POSIX_DO( pthread_mutex_unlock( &item->lock ), ); return ret; } /* Unlink */ uti_list_unlink(&item->list); /* Destroy the mutex */ CHECK_POSIX( pthread_mutex_unlock( &item->lock ) ); CHECK_POSIX( pthread_mutex_destroy( &item->lock ) ); /* Free the object */ free(item); return 0; } /********************************************************************************************************/ /* Initialize the module */ int sess_init ( void ) { int i; TRACE_ENTRY( "" ); /* Initialize the hash table */ for (i = 0; i < sizeof(sess_hash) / sizeof(sess_hash[0]); i++) { CHECK_POSIX( pthread_mutex_init(&sess_hash[i].lock, NULL) ); uti_list_init( &sess_hash[i].sentinel, NULL ); sess_hash[i].count = 0; } /* Initialize the sentinel for registered clients */ uti_list_init( &_srt_sentinel, NULL ); /* and its lock */ CHECK_POSIX( pthread_mutex_init(&_str_lock, NULL) ); /* Initialize the global counters */ g_sid_h = (uint32_t) time(NULL); g_sid_l = 0; return 0; } /* Find a session object corresponding to a given session-id / or create it */ int sess_fromsid ( char * sid, size_t len, sess_id_t ** session, int * new ) { int ret = 0; uti_list_t *li = NULL; uint32_t hash; TRACE_ENTRY( "%p %d %p", sid, len, session ); /* Validate the parameters */ CHECK_PARAMS( (sid != NULL) && (len != 0) && (session != NULL) ); /* Compute the hash value of this string */ hash = uti_hash(sid, len); /* Lock the hash table line */ CHECK_POSIX( pthread_mutex_lock( H_LOCK(hash) ) ); /* Search in the table */ ret = find_si( hash, sid, len, H_LIST(hash), &li); if (ret == 0) { /* The element is found in the table */ if (new) *new = 0; /* increment its refcount, as if sess_link had been called */ _SI(li)->rc++; /* Save the value that must be returned */ *session = (sess_id_t *)li; } else { _sess_id_t * newsi; char * sidcpy; /* The element did not exist already, li points to the previous element */ /* Create a new element -- how to do this outside the mutex section, and efficiently? */ CHECK_MALLOC_DO( sidcpy = malloc(len + 1), { ret = ENOMEM; goto end; } ); CHECK_MALLOC_DO( newsi = malloc(sizeof(_sess_id_t)), { ret = ENOMEM; free(sidcpy); goto end; } ); /* Initialize the element content */ memset(newsi, 0, sizeof(_sess_id_t)); uti_list_init( &newsi->chain, sidcpy ); newsi->hash = hash; newsi->eyec = _SI_EYEC; memcpy(sidcpy, sid, len); sidcpy[len] = '\0'; newsi->rc = 1; CHECK_POSIX_DO( ret = clock_gettime(CLOCK_REALTIME, &newsi->ts), { free(sidcpy); free(newsi); goto end; } ); /* Store this element in the list */ uti_list_insert_after(li, &newsi->chain); /* Increment the counter */ H_COUNT(hash) += 1; /* Save the reference to it */ *session = (sess_id_t *)newsi; if (new) *new = 1; } end: /* Finish, unlock the line */ CHECK_POSIX( pthread_mutex_unlock( H_LOCK(hash) ) ); return ret; } /* Create a new session (identifier and object). */ int sess_new ( sess_id_t ** session, sess_flags_t flags, char * opt ) { int ret = 0; int new = 0; char * str = NULL; size_t strsz = 0; int mustfree = 0; TRACE_ENTRY( "%p %d %p", session, flags, opt ); /* Check the parameters */ CHECK_PARAMS( (session != NULL) ); /* Ok, first create the identifier following the flags */ switch (flags) { case SESSION_NEW_DEFAULT: /* "<diameterId>;<high32>;<low32>[;opt]" */ { int len; /* Compute the size of the new string */ len = g_conf->diamid_len; len += 2 * ( 1 /* ';' */ + 10 /* uint32_t max = 4294967295 = 10 digits */ ); if (opt) len += 1 + strlen(opt); len++; /* for the final '\0' in the worst case */ /* Allocate the space for the buffer */ CHECK_MALLOC( str = malloc(len) ); mustfree = 1; memset(str, 0, len); /* Increment the value of the low32 */ CHECK_POSIX_DO( ret = pthread_mutex_lock(&g_sid_lock), goto end ); ++g_sid_l; /* Now write the new value */ if (opt) strsz = snprintf(str, len, "%s;%u;%u;%s", g_pconf->diameter_identity, g_sid_h, g_sid_l, opt); else strsz = snprintf(str, len, "%s;%u;%u", g_pconf->diameter_identity, g_sid_h, g_sid_l); CHECK_POSIX_DO( ret = pthread_mutex_unlock(&g_sid_lock), goto end ); } break; case SESSION_NEW_SEQUENCE: /* "<diameterId>;opt" */ CHECK_PARAMS( (opt != NULL) ); { int len; /* Compute the size of the new string */ len = g_conf->diamid_len + 1 + strlen(opt) + 1; /* +1 for the ';' and the final '\0' */ /* Allocate the space for the buffer */ CHECK_MALLOC( str = malloc(len) ); mustfree = 1; memset(str, 0, len); strsz = snprintf(str, len, "%s;%s", g_pconf->diameter_identity, opt); } break; case SESSION_NEW_FULL: /* "opt" */ CHECK_PARAMS( (opt != NULL) ); str = opt; strsz = strlen(str); break; case SESSION_NEW_OTHER: /* "<otherId>;<high32>;<low32>[;opt]" */ CHECK_PARAMS( (opt != NULL) ); { int len; char * secopt = NULL; char * dupopt; /* so that we can modify the string in case it's a constant */ CHECK_MALLOC( dupopt = strdup(opt) ); /* Compute the size of the new string */ len = strlen(dupopt); len += 2 * ( 1 /* ';' */ + 10 /* uint32_t max = 4294967295 = 10 digits */ ); len++; /* for the final '\0' in the worst case */ /* Allocate the space for the buffer */ CHECK_MALLOC( str = malloc(len) ); mustfree = 1; memset(str, 0, len); /* Increment the value of the low32 */ CHECK_POSIX_DO( ret = pthread_mutex_lock(&g_sid_lock), goto end ); ++g_sid_l; /* Search a ';' in opt */ secopt = strchr(dupopt, ';'); /* Now write the new value */ if (secopt) { *secopt = '\0'; secopt++; strsz = snprintf(str, len, "%s;%u;%u;%s", dupopt, g_sid_h, g_sid_l, secopt); } else { strsz = snprintf(str, len, "%s;%u;%u", dupopt, g_sid_h, g_sid_l); } CHECK_POSIX_DO( ret = pthread_mutex_unlock(&g_sid_lock), goto end ); free(dupopt); } break; default: CHECK_PARAMS( (flags, 0) ); } /* Now str points to the new session string. Create the new session */ TRACE_DEBUG(FULL, "Creating session object for sid: '%s'", str); CHECK_FCT_DO( ret = sess_fromsid( str, strsz, session, &new ), goto end ); /* Check that the object did not already exist before */ if (new == 0) { TRACE_DEBUG(INFO, "The session created already existed => The opt parameter must have been bad"); /* The string we received was not really a new unique value */ CHECK_FCT_DO( sess_unlink(session), goto end ); ret = EALREADY; } end: if (mustfree) free(str); return ret; } /* Get the session-id string of a session */ int sess_getsid ( sess_id_t * session, char ** sid ) { TRACE_ENTRY( "%p %p", session, sid ); /* Check the parameters */ CHECK_PARAMS( VALIDATE_SI(session) && (sid != NULL) ); /* Write the location and exit */ *sid = _SID(session); return 0; } /* Increase refcount of a session */ int sess_link ( sess_id_t * session ) { TRACE_ENTRY( "%p", session ); /* Check the parameter */ CHECK_PARAMS( VALIDATE_SI(session) ); /* Lock the list entry */ CHECK_POSIX( pthread_mutex_lock(H_LOCK(_SI(session)->hash)) ); /* Increment the refcount */ _SI(session)->rc++; /* Unlock the list entry */ CHECK_POSIX( pthread_mutex_unlock(H_LOCK(_SI(session)->hash)) ); return 0; } /* Decrease refcount of a session, eventually free it. */ int sess_unlink ( sess_id_t ** session ) { uint32_t hash; TRACE_ENTRY( "%p", session ); /* Check the parameter */ CHECK_PARAMS( session && VALIDATE_SI(*session) ); /* Copy the hash value */ hash = _SI(*session)->hash; /* Lock the list entry */ CHECK_POSIX( pthread_mutex_lock(H_LOCK(hash)) ); /* Decrement the refcount */ _SI(*session)->rc--; /* If the count is now 0, we destroy the object */ if (_SI(*session)->rc == 0) { _SI(*session)->eyec = 0xdead; /* Unlink the element from the chain */ uti_list_unlink(&_SI(*session)->chain); /* Update the count */ H_COUNT(hash) -= 1; /* Now free the element */ if (_SID(*session)) free(_SID(*session)); free(*session); } /* Unlock the list entry */ CHECK_POSIX( pthread_mutex_unlock(H_LOCK(hash)) ); /* Update the location for the calling function */ *session = NULL; return 0; } /* Create a new client of the module. */ int sess_regext ( sess_reg_t ** client ) { int ret = 0; _sess_reg_t * new = NULL; TRACE_ENTRY( "%p", client ); /* Check the parameter */ CHECK_PARAMS( client != NULL ); /* Create a new object */ CHECK_MALLOC( new = (_sess_reg_t *) malloc(sizeof(_sess_reg_t)) ); /* Initialize the object */ memset(new, 0, sizeof(_sess_reg_t)); uti_list_init(&new->list, NULL); new->eyec = _SR_EYEC; CHECK_POSIX_DO( ret = pthread_mutex_init(&new->lock, NULL), { free(new); return ret; } ); uti_list_init(&new->sentinel, NULL); /* Save the new object in the list */ CHECK_POSIX_DO( ret = pthread_mutex_lock(&_str_lock), { free(new); return ret; } ); uti_list_insert_after( &_srt_sentinel, &new->list); CHECK_POSIX( pthread_mutex_unlock(&_str_lock) ); /* Copy the reference to the calling process */ *client = (sess_reg_t *)new; return 0; } /* Destroy a client of the module. */ int sess_deregext ( sess_reg_t * client ) { int ret = 0; TRACE_ENTRY( "%p", client ); CHECK_PARAMS( VALIDATE_SR(client) ); /* Lock the list of clients */ CHECK_POSIX( pthread_mutex_lock( &_str_lock ) ); /* Now destroy the client handler */ CHECK_FCT_DO( ret = _destroy_sr(_SR(client)), { CHECK_POSIX(pthread_mutex_unlock( &_str_lock )); return ret; } ); /* Unlock the list of clients */ CHECK_POSIX( pthread_mutex_unlock( &_str_lock ) ); return 0; } /* Register data in a session. */ int sess_data_reg ( sess_id_t * session, sess_reg_t * client, void * data, void (*cleanup)(void *) ) { int ret = 0; _sd_data_t *new = NULL; uti_list_t *prev = NULL; TRACE_ENTRY( "%p %p %p %p", session, client, data, cleanup ); /* Check parameters */ CHECK_PARAMS( VALIDATE_SI(session) && VALIDATE_SR(client) && (data != NULL) ); /* Create a new object to hold the data */ CHECK_MALLOC( new = (_sd_data_t *) malloc(sizeof(_sd_data_t)) ); memset(new, 0, sizeof(_sd_data_t)); uti_list_init(&new->list, (void *)session); new->clncb = cleanup; new->data = data; /* Link the session */ CHECK_FCT_DO( ret = sess_link(_SDI(new)), { free(new); return ret; } ); /* Lock the client object */ CHECK_POSIX_DO( ret = pthread_mutex_lock( &_SR(client)->lock ), { CHECK_FCT_DO( sess_unlink((sess_id_t **)&_LIST(new)->o), /* no fallback */ ); free(new); return ret; } ); /* Find the place in the list */ ret = find_sd(_SDI(new), &_SR(client)->sentinel, &prev); if (ret == 0) { /* There is already data associated with this session and client */ TRACE_DEBUG(INFO, "Data already associated with session/client."); CHECK_FCT_DO( sess_unlink((sess_id_t **)&_LIST(new)->o), /* no fallback */ ); free(new); CHECK_POSIX( pthread_mutex_unlock( &_SR(client)->lock ) ); return EALREADY; } /* Now "prev" is the previous element where the new element must be put */ uti_list_insert_after(prev, &new->list); /* Unlock, we're done */ CHECK_POSIX( pthread_mutex_unlock( &_SR(client)->lock ) ); return 0; } /* Retrieve registered data. */ int sess_data_get ( sess_id_t * session, sess_reg_t * client, void ** data ) { int ret = 0; uti_list_t *item = NULL; TRACE_ENTRY( "%p %p %p", session, client, data ); /* Check parameters */ CHECK_PARAMS( VALIDATE_SI(session) && VALIDATE_SR(client) && (data != NULL) ); *data = NULL; /* Lock the client object */ CHECK_POSIX( pthread_mutex_lock( &_SR(client)->lock ) ); /* Find the data */ ret = find_sd(_SI(session), &_SR(client)->sentinel, &item); if (ret == 0) { *data = _SD(item)->data; } /* Unlock, we're done */ CHECK_POSIX( pthread_mutex_unlock( &_SR(client)->lock ) ); return (*data == NULL) ? ENOENT : 0; } /* Remove registered data from a session. */ int sess_data_dereg ( sess_id_t * session, sess_reg_t * client, void ** data ) { int ret = 0; int noent = 0; uti_list_t *item = NULL; TRACE_ENTRY( "%p %p %p", session, client, data ); /* Check parameters */ CHECK_PARAMS( VALIDATE_SI(session) && VALIDATE_SR(client) ); if (data) *data = NULL; /* Lock the client object */ CHECK_POSIX( pthread_mutex_lock( &_SR(client)->lock ) ); /* Find the data */ ret = find_sd(_SI(session), &_SR(client)->sentinel, &item); if (ret == 0) { CHECK_FCT_DO( ret = _destroy_sr_data(_SD(item), data), { CHECK_POSIX( pthread_mutex_unlock( &_SR(client)->lock ) ); return ret; } ); } else { TRACE_DEBUG(INFO, "data not found."); noent = 1; } /* Unlock, we're done */ CHECK_POSIX( pthread_mutex_unlock( &_SR(client)->lock ) ); return noent ? ENOENT : 0; } /* Destroy a session and any registered data */ int sess_destroy( sess_id_t ** session ) { int ret = 0; uti_list_t *li, *item = NULL; TRACE_ENTRY( "%p", session ); /* Check the parameter */ CHECK_PARAMS( session && VALIDATE_SI(*session) ); /* Lock the global list of handlers */ CHECK_POSIX( pthread_mutex_lock(&_str_lock) ); for (li = _srt_sentinel.next; li != &_srt_sentinel; li = li->next) { /* Lock the sublist */ CHECK_POSIX( pthread_mutex_lock( &_SR(li)->lock ) ); /* Search for data registered for this session in this handler */ ret = find_sd(_SI(*session), &_SR(li)->sentinel, &item); /* If found, destroy it */ if (ret == 0) { CHECK_FCT( _destroy_sr_data(_SD(item), NULL) ) } CHECK_POSIX( pthread_mutex_unlock( &_SR(li)->lock ) ); } CHECK_POSIX( pthread_mutex_unlock(&_str_lock) ); /* Now unlink the session. It will be freed unless someone had called "sess_link" explicitely. */ return sess_unlink(session); } /* End of the module */ int sess_fini ( void ) { int ret = 0; int i; TRACE_ENTRY( "" ); /* Lock the list of clients */ CHECK_POSIX( pthread_mutex_lock( &_str_lock ) ); /* Destroy all the registered clients */ while (!IS_LIST_EMPTY( &_srt_sentinel )) { TRACE_DEBUG( FULL, "Destroying module %p...", _srt_sentinel.next ); CHECK_FCT_DO( ret = _destroy_sr( _SR(_srt_sentinel.next) ), { CHECK_POSIX( pthread_mutex_unlock( &_str_lock ) ); return ret; } ); } /* Unlock the list of clients */ CHECK_POSIX( pthread_mutex_unlock( &_str_lock ) ); /* Destroy the lock */ CHECK_POSIX( pthread_mutex_destroy( &_str_lock ) ); /* Check that there is no more session registered */ for (i = 0; i < sizeof(sess_hash) / sizeof(sess_hash[0]); i++) { if (!IS_LIST_EMPTY(&sess_hash[i].sentinel)) { TRACE_DEBUG(INFO, "List of session (%d) is not empty. Maybe messages are still refering sessions? EBUSY", i); return EBUSY; } CHECK_POSIX( pthread_mutex_destroy( &sess_hash[i].lock ) ); } /* Done! */ return 0; } /********************************************************************************************************/ /* debug functions */ static void _dump_si ( _sess_id_t * si, int indent ) { TRACE_DEBUG(FULL, "%*ssession @%p: h:%8x rc:%d ts:%d sid:'%s'", indent, "", si, si->hash, si->rc, si->ts.tv_sec, _SID(si)); } static void _dump_hash (void) { int i; TRACE_DEBUG(FULL, " ======= Dumping the session hash table ======= "); for (i=0; i < sizeof(sess_hash) / sizeof(sess_hash[0]); i++) { uti_list_t * item; TRACE_DEBUG(FULL, " l:%02d cnt:%d", i, sess_hash[i].count); for (item = sess_hash[i].sentinel.next; item != &sess_hash[i].sentinel; item = item->next) _dump_si(_SI(item), 3); } TRACE_DEBUG(FULL, " =======/end of session hash table dump/======= "); } static void _dump_sd ( _sd_data_t * sd, int indent ) { TRACE_DEBUG(FULL, "%*sdata @%p: data:%p clncb:%p sid:'%s'", indent, "", sd, sd->data, sd->clncb, _SID(_SDI(sd))); } static void _dump_regs (void) { uti_list_t * client; uti_list_t * data; TRACE_DEBUG(FULL, " ======= Dumping the registered clients list ======= "); for (client = _srt_sentinel.next; client != &_srt_sentinel; client = client->next) { TRACE_DEBUG(FULL, "client @%p:", client); for (data = _SR(client)->sentinel.next; data != &_SR(client)->sentinel; data = data->next) { _dump_sd(_SD(data), 3); } } TRACE_DEBUG(FULL, " =======/end of registered clients list dump/======= "); } void sess_dump(void) { _dump_hash(); _dump_regs(); }