diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2024-08-08 14:34:10 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2024-08-08 14:34:10 -0300 |
| commit | 56f96f0bdc6187ce19064d311c000656ae68008b (patch) | |
| tree | 104a7843861befbd4da34a66f03b85d7b9ea0df9 /libfdproto | |
| parent | 3e8d3ed13f58af810934de696c34bf5bf16ddcc6 (diff) | |
New upstream version 1.5.0upstream
Diffstat (limited to 'libfdproto')
| -rw-r--r-- | libfdproto/dictionary-internal.h | 136 | ||||
| -rw-r--r-- | libfdproto/dictionary.c | 743 | ||||
| -rw-r--r-- | libfdproto/dictionary_functions.c | 4 | ||||
| -rw-r--r-- | libfdproto/fifo.c | 292 | ||||
| -rw-r--r-- | libfdproto/log.c | 3 | ||||
| -rw-r--r-- | libfdproto/messages.c | 33 | ||||
| -rw-r--r-- | libfdproto/sessions.c | 316 |
7 files changed, 821 insertions, 706 deletions
diff --git a/libfdproto/dictionary-internal.h b/libfdproto/dictionary-internal.h new file mode 100644 index 0000000..47842b6 --- /dev/null +++ b/libfdproto/dictionary-internal.h @@ -0,0 +1,136 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@freediameter.net> * +* * +* Copyright (c) 2020, 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. * +*********************************************************************************************************/ + +#ifndef HAD_DICTIONARY_INTERNAL_H +#define HAD_DICTIONARY_INTERNAL_H + +/* Names of the base types */ +extern const char * type_base_name[]; + +/* The number of lists in an object */ +#define NB_LISTS_PER_OBJ 3 + +/* Definition of the dictionary objects */ +struct dict_object { + enum dict_object_type type; /* What type of object is this? */ + int objeyec;/* eyecatcher for this object */ + int typeyec;/* eyecatcher for this type of object */ + struct dictionary *dico; /* The dictionary this object belongs to */ + + union { + struct dict_vendor_data vendor; /* datastr_len = strlen(vendor_name) */ + struct dict_application_data application; /* datastr_len = strlen(application_name) */ + struct dict_type_data type; /* datastr_len = strlen(type_name) */ + struct dict_enumval_data enumval; /* datastr_len = strlen(enum_name) */ + struct dict_avp_data avp; /* datastr_len = strlen(avp_name) */ + struct dict_cmd_data cmd; /* datastr_len = strlen(cmd_name) */ + struct dict_rule_data rule; /* datastr_len = 0 */ + } data; /* The data of this object */ + + size_t datastr_len; /* cached length of the string inside the data. Saved when the object is created. */ + + struct dict_object * parent; /* The parent of this object, if any */ + + struct fd_list list[NB_LISTS_PER_OBJ];/* used to chain objects.*/ + /* More information about the lists : + + - the use for each list depends on the type of object. See detail below. + + - a sentinel for a list has its 'o' field cleared. (this is the criteria to detect end of a loop) + + - The lists are always ordered. The criteria are described below. the functions to order them are referenced in dict_obj_info + + - The dict_lock must be held for any list operation. + + => VENDORS: + list[0]: list of the vendors, ordered by their id. The sentinel is g_dict_vendors (vendor with id 0) + list[1]: sentinel for the list of AVPs from this vendor, ordered by AVP code. + list[2]: sentinel for the list of AVPs from this vendor, ordered by AVP name (fd_os_cmp). + + => APPLICATIONS: + list[0]: list of the applications, ordered by their id. The sentinel is g_dict_applications (application with id 0) + list[1]: not used + list[2]: not used. + + => TYPES: + list[0]: list of the types, ordered by their names. The sentinel is g_list_types. + list[1]: sentinel for the type_enum list of this type, ordered by their constant name (fd_os_cmp). + list[2]: sentinel for the type_enum list of this type, ordered by their constant value. + + => TYPE_ENUMS: + list[0]: list of the contants for a given type, ordered by the constant name (fd_os_cmp). Sentinel is a (list[1]) element of a TYPE object. + list[1]: list of the contants for a given type, ordered by the constant value. Sentinel is a (list[2]) element of a TYPE object. + list[2]: not used + + => AVPS: + list[0]: list of the AVP from a given vendor, ordered by avp code. Sentinel is a list[1] element of a VENDOR object. + list[1]: list of the AVP from a given vendor, ordered by avp name (fd_os_cmp). Sentinel is a list[2] element of a VENDOR object. + list[2]: sentinel for the rule list that apply to this AVP. + + => COMMANDS: + list[0]: list of the commands, ordered by their names (fd_os_cmp). The sentinel is g_list_cmd_name. + list[1]: list of the commands, ordered by their command code and 'R' flag. The sentinel is g_list_cmd_code. + list[2]: sentinel for the rule list that apply to this command. + + => RULES: + list[0]: list of the rules for a given (grouped) AVP or Command, ordered by the AVP vendor & code to which they refer. sentinel is list[2] of a command or (grouped) avp. + list[1]: not used + list[2]: not used. + + */ + + /* Sentinel for the dispatch callbacks */ + struct fd_list disp_cbs; + +}; + +/* Definition of the dictionary structure */ +struct dictionary { + int dict_eyec; /* Eye-catcher for the dictionary (DICT_EYECATCHER) */ + + pthread_rwlock_t dict_lock; /* The global rwlock for the dictionary */ + + struct dict_object dict_vendors; /* Sentinel for the list of vendors, corresponding to vendor 0 */ + struct dict_object dict_applications; /* Sentinel for the list of applications, corresponding to app 0 */ + struct fd_list dict_types; /* Sentinel for the list of types */ + struct fd_list dict_cmd_name; /* Sentinel for the list of commands, ordered by names */ + struct fd_list dict_cmd_code; /* Sentinel for the list of commands, ordered by codes */ + + struct dict_object dict_cmd_error; /* Special command object for answers with the 'E' bit set */ + + int dict_count[DICT_TYPE_MAX + 1]; /* Number of objects of each type */ +}; + +#endif /* HAD_DICTIONARY_INTERNAL_H */ diff --git a/libfdproto/dictionary.c b/libfdproto/dictionary.c index 49043ed..9476555 100644 --- a/libfdproto/dictionary.c +++ b/libfdproto/dictionary.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2015, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -34,6 +34,7 @@ *********************************************************************************************************/ #include "fdproto-internal.h" +#include "dictionary-internal.h" #include <inttypes.h> /* Names of the base types */ @@ -48,104 +49,10 @@ const char * type_base_name[] = { /* must keep in sync with dict_avp_basetype */ "FLOAT64" /* AVP_TYPE_FLOAT64 */ }; -/* The number of lists in an object */ -#define NB_LISTS_PER_OBJ 3 - /* Some eye catchers definitions */ #define OBJECT_EYECATCHER (0x0b13c7) #define DICT_EYECATCHER (0x00d1c7) -/* Definition of the dictionary objects */ -struct dict_object { - enum dict_object_type type; /* What type of object is this? */ - int objeyec;/* eyecatcher for this object */ - int typeyec;/* eyecatcher for this type of object */ - struct dictionary *dico; /* The dictionary this object belongs to */ - - union { - struct dict_vendor_data vendor; /* datastr_len = strlen(vendor_name) */ - struct dict_application_data application; /* datastr_len = strlen(application_name) */ - struct dict_type_data type; /* datastr_len = strlen(type_name) */ - struct dict_enumval_data enumval; /* datastr_len = strlen(enum_name) */ - struct dict_avp_data avp; /* datastr_len = strlen(avp_name) */ - struct dict_cmd_data cmd; /* datastr_len = strlen(cmd_name) */ - struct dict_rule_data rule; /* datastr_len = 0 */ - } data; /* The data of this object */ - - size_t datastr_len; /* cached length of the string inside the data. Saved when the object is created. */ - - struct dict_object * parent; /* The parent of this object, if any */ - - struct fd_list list[NB_LISTS_PER_OBJ];/* used to chain objects.*/ - /* More information about the lists : - - - the use for each list depends on the type of object. See detail below. - - - a sentinel for a list has its 'o' field cleared. (this is the criteria to detect end of a loop) - - - The lists are always ordered. The criteria are described below. the functions to order them are referenced in dict_obj_info - - - The dict_lock must be held for any list operation. - - => VENDORS: - list[0]: list of the vendors, ordered by their id. The sentinel is g_dict_vendors (vendor with id 0) - list[1]: sentinel for the list of AVPs from this vendor, ordered by AVP code. - list[2]: sentinel for the list of AVPs from this vendor, ordered by AVP name (fd_os_cmp). - - => APPLICATIONS: - list[0]: list of the applications, ordered by their id. The sentinel is g_dict_applications (application with id 0) - list[1]: not used - list[2]: not used. - - => TYPES: - list[0]: list of the types, ordered by their names. The sentinel is g_list_types. - list[1]: sentinel for the type_enum list of this type, ordered by their constant name (fd_os_cmp). - list[2]: sentinel for the type_enum list of this type, ordered by their constant value. - - => TYPE_ENUMS: - list[0]: list of the contants for a given type, ordered by the constant name (fd_os_cmp). Sentinel is a (list[1]) element of a TYPE object. - list[1]: list of the contants for a given type, ordered by the constant value. Sentinel is a (list[2]) element of a TYPE object. - list[2]: not used - - => AVPS: - list[0]: list of the AVP from a given vendor, ordered by avp code. Sentinel is a list[1] element of a VENDOR object. - list[1]: list of the AVP from a given vendor, ordered by avp name (fd_os_cmp). Sentinel is a list[2] element of a VENDOR object. - list[2]: sentinel for the rule list that apply to this AVP. - - => COMMANDS: - list[0]: list of the commands, ordered by their names (fd_os_cmp). The sentinel is g_list_cmd_name. - list[1]: list of the commands, ordered by their command code and 'R' flag. The sentinel is g_list_cmd_code. - list[2]: sentinel for the rule list that apply to this command. - - => RULES: - list[0]: list of the rules for a given (grouped) AVP or Command, ordered by the AVP vendor & code to which they refer. sentinel is list[2] of a command or (grouped) avp. - list[1]: not used - list[2]: not used. - - */ - - /* Sentinel for the dispatch callbacks */ - struct fd_list disp_cbs; - -}; - -/* Definition of the dictionary structure */ -struct dictionary { - int dict_eyec; /* Eye-catcher for the dictionary (DICT_EYECATCHER) */ - - pthread_rwlock_t dict_lock; /* The global rwlock for the dictionary */ - - struct dict_object dict_vendors; /* Sentinel for the list of vendors, corresponding to vendor 0 */ - struct dict_object dict_applications; /* Sentinel for the list of applications, corresponding to app 0 */ - struct fd_list dict_types; /* Sentinel for the list of types */ - struct fd_list dict_cmd_name; /* Sentinel for the list of commands, ordered by names */ - struct fd_list dict_cmd_code; /* Sentinel for the list of commands, ordered by codes */ - - struct dict_object dict_cmd_error; /* Special command object for answers with the 'E' bit set */ - - int dict_count[DICT_TYPE_MAX + 1]; /* Number of objects of each type */ -}; - /* Forward declarations of dump functions */ static DECLARE_FD_DUMP_PROTOTYPE(dump_vendor_data, void * data ); static DECLARE_FD_DUMP_PROTOTYPE(dump_application_data, void * data ); @@ -177,32 +84,32 @@ static struct { int haslist[NB_LISTS_PER_OBJ]; /* Tell if this list is used */ } dict_obj_info[] = { { 0, "(error)", 0, 0, 0, 0, NULL, NULL, {0, 0, 0} } - /* type name datasize parent parenttype + /* type name datasize parent parenttype eyecatcher dump_data search_fct, haslist[] */ ,{ DICT_VENDOR, "VENDOR", sizeof(struct dict_vendor_data), 0, 0, OBJECT_EYECATCHER + 1, dump_vendor_data, search_vendor, { 1, 0, 0 } } - + ,{ DICT_APPLICATION, "APPLICATION", sizeof(struct dict_application_data), 1, DICT_VENDOR, OBJECT_EYECATCHER + 2, dump_application_data, search_application, { 1, 0, 0 } } - + ,{ DICT_TYPE, "TYPE", sizeof(struct dict_type_data), 1, DICT_APPLICATION, OBJECT_EYECATCHER + 3, dump_type_data, search_type, { 1, 0, 0 } } - + ,{ DICT_ENUMVAL, "ENUMVAL", sizeof(struct dict_enumval_data), 2, DICT_TYPE, OBJECT_EYECATCHER + 4, NULL, search_enumval, { 1, 1, 0 } } - + ,{ DICT_AVP, "AVP", sizeof(struct dict_avp_data), 1, DICT_TYPE, OBJECT_EYECATCHER + 5, dump_avp_data, search_avp, { 1, 1, 0 } } - + ,{ DICT_COMMAND, "COMMAND", sizeof(struct dict_cmd_data), 1, DICT_APPLICATION, OBJECT_EYECATCHER + 6, dump_command_data, search_cmd, { 1, 1, 0 } } - + ,{ DICT_RULE, "RULE", sizeof(struct dict_rule_data), 2, -1 /* special case: grouped avp or command */, OBJECT_EYECATCHER + 7, dump_rule_data, search_rule, { 1, 0, 0 } } - + }; - + /* Macro to verify a "type" value */ #define CHECK_TYPE( type ) ( ((type) > 0) && ((type) <= DICT_TYPE_MAX) ) @@ -230,17 +137,17 @@ static struct { *(plen) = strlen((str)); \ str = os0dup( str, *(plen)); \ } - + /* Initialize an object */ static void init_object( struct dict_object * obj, enum dict_object_type type ) { int i; - + TRACE_ENTRY("%p %d", obj, type); - + /* Clean the object first */ memset ( obj, 0, sizeof(struct dict_object)); - + CHECK_PARAMS_DO( CHECK_TYPE(type), return ); obj->type = type; @@ -248,59 +155,66 @@ static void init_object( struct dict_object * obj, enum dict_object_type type ) obj->typeyec = _OBINFO(obj).eyecatcher; /* We don't initialize the data nor the parent here */ - + /* Now init the lists */ for (i=0; i<NB_LISTS_PER_OBJ; i++) { - if (_OBINFO(obj).haslist[i] != 0) + if (_OBINFO(obj).haslist[i] != 0) fd_list_init(&obj->list[i], obj); else fd_list_init(&obj->list[i], NULL); } - + fd_list_init(&obj->disp_cbs, NULL); } /* Initialize the "data" part of an object */ -static int init_object_data(struct dict_object * dest, void * source, enum dict_object_type type) +static int init_object_data(struct dict_object * dest, void * source, enum dict_object_type type, int dupos) { TRACE_ENTRY("%p %p %d", dest, source, type); CHECK_PARAMS( dest && source && CHECK_TYPE(type) ); - - /* Generic: copy the full data structure */ + + /* Generic: copy the full data structure */ memcpy( &dest->data, source, dict_obj_info[type].datasize ); - + /* Then strings must be duplicated, not copied */ /* This function might be simplified by always defining the "name" field as the first field of the structures, but... it's error-prone */ switch (type) { case DICT_VENDOR: DUP_string_len( dest->data.vendor.vendor_name, &dest->datastr_len ); break; - + case DICT_APPLICATION: DUP_string_len( dest->data.application.application_name, &dest->datastr_len ); break; - + case DICT_TYPE: DUP_string_len( dest->data.type.type_name, &dest->datastr_len ); break; - + case DICT_ENUMVAL: DUP_string_len( dest->data.enumval.enum_name, &dest->datastr_len ); + if (dupos) { + // we also need to duplicate the octetstring constant value since it is a pointer. + dest->data.enumval.enum_value.os.data = os0dup( + ((struct dict_enumval_data *)source)->enum_value.os.data, + ((struct dict_enumval_data *)source)->enum_value.os.len + ); + } break; case DICT_AVP: DUP_string_len( dest->data.avp.avp_name, &dest->datastr_len ); break; - + case DICT_COMMAND: DUP_string_len( dest->data.cmd.cmd_name, &dest->datastr_len ); break; - + default: /* Nothing to do for RULES */ ; } - + return 0; } @@ -308,7 +222,7 @@ static int init_object_data(struct dict_object * dest, void * source, enum dict_ static int verify_object( struct dict_object * obj ) { TRACE_ENTRY("%p", obj); - + CHECK_PARAMS_DO( obj && (obj->objeyec == OBJECT_EYECATCHER) && CHECK_TYPE(obj->type) @@ -326,7 +240,7 @@ static int verify_object( struct dict_object * obj ) } return 0; } ); - + /* The object is probably valid. */ return 1; } @@ -335,20 +249,20 @@ static int verify_object( struct dict_object * obj ) static void destroy_object_data(struct dict_object * obj) { /* TRACE_ENTRY("%p", obj); */ - + switch (obj->type) { case DICT_VENDOR: free( obj->data.vendor.vendor_name ); break; - + case DICT_APPLICATION: free( obj->data.application.application_name ); break; - + case DICT_TYPE: free( obj->data.type.type_name ); break; - + case DICT_ENUMVAL: free( obj->data.enumval.enum_name ); break; @@ -356,11 +270,11 @@ static void destroy_object_data(struct dict_object * obj) case DICT_AVP: free( obj->data.avp.avp_name ); break; - + case DICT_COMMAND: free( obj->data.cmd.cmd_name ); break; - + default: /* nothing to do */ ; @@ -371,10 +285,10 @@ static void destroy_object_data(struct dict_object * obj) static void destroy_object(struct dict_object * obj); /* Destroy all objects in a list - the lock must be held */ -static void destroy_list(struct fd_list * head) +static void destroy_list(struct fd_list * head) { /* TRACE_ENTRY("%p", head); */ - + /* loop in the list */ while (!FD_IS_LIST_EMPTY(head)) { @@ -382,24 +296,24 @@ static void destroy_list(struct fd_list * head) destroy_object(_O(head->next->o)); } } - + /* Free an object and its sublists */ static void destroy_object(struct dict_object * obj) { int i; - + /* TRACE_ENTRY("%p", obj); */ - + /* Update global count */ - if (obj->dico) + if (obj->dico) obj->dico->dict_count[obj->type]--; - + /* Mark the object as invalid */ obj->objeyec = 0xdead; - + /* First, destroy the data associated to the object */ destroy_object_data(obj); - + for (i=0; i<NB_LISTS_PER_OBJ; i++) { if (_OBINFO(obj).haslist[i]) /* unlink the element from the list */ @@ -408,14 +322,14 @@ static void destroy_object(struct dict_object * obj) /* This is either a sentinel or unused (=emtpy) list, let's destroy it */ destroy_list( &obj->list[i] ); } - + /* Unlink all elements from the dispatch list; they will be freed when callback is unregistered */ CHECK_POSIX_DO( pthread_rwlock_wrlock(&fd_disp_lock), /* continue */ ); while (!FD_IS_LIST_EMPTY(&obj->disp_cbs)) { fd_list_unlink( obj->disp_cbs.next ); } CHECK_POSIX_DO( pthread_rwlock_unlock(&fd_disp_lock), /* continue */ ); - + /* Last, destroy the object */ free(obj); } @@ -430,14 +344,14 @@ static void destroy_object(struct dict_object * obj) /* Compare two values */ #define ORDER_scalar( i1, i2 ) \ - ((i1 < i2 ) ? -1 : ( i1 > i2 ? 1 : 0 )) + ((i1 < i2 ) ? -1 : ( i1 > i2 ? 1 : 0 )) /* Compare two vendor objects by their id (checks already performed) */ static int order_vendor_by_id ( struct dict_object *o1, struct dict_object *o2 ) { TRACE_ENTRY("%p %p", o1, o2); - + return ORDER_scalar( o1->data.vendor.vendor_id, o2->data.vendor.vendor_id ); } @@ -445,7 +359,7 @@ static int order_vendor_by_id ( struct dict_object *o1, struct dict_object *o2 ) static int order_appli_by_id ( struct dict_object *o1, struct dict_object *o2 ) { TRACE_ENTRY("%p %p", o1, o2); - + return ORDER_scalar( o1->data.application.application_id, o2->data.application.application_id ); } @@ -453,7 +367,7 @@ static int order_appli_by_id ( struct dict_object *o1, struct dict_object *o2 ) static int order_type_by_name ( struct dict_object *o1, struct dict_object *o2 ) { TRACE_ENTRY("%p %p", o1, o2); - + return fd_os_cmp( o1->data.type.type_name, o1->datastr_len, o2->data.type.type_name, o2->datastr_len ); } @@ -461,7 +375,7 @@ static int order_type_by_name ( struct dict_object *o1, struct dict_object *o2 ) static int order_enum_by_name ( struct dict_object *o1, struct dict_object *o2 ) { TRACE_ENTRY("%p %p", o1, o2); - + return fd_os_cmp( o1->data.enumval.enum_name, o1->datastr_len, o2->data.enumval.enum_name, o2->datastr_len ); } @@ -469,13 +383,13 @@ static int order_enum_by_name ( struct dict_object *o1, struct dict_object *o2 ) static int order_enum_by_val ( struct dict_object *o1, struct dict_object *o2 ) { TRACE_ENTRY("%p %p", o1, o2); - + /* The comparison function depends on the type of data */ switch ( o1->parent->data.type.type_base ) { case AVP_TYPE_OCTETSTRING: - return fd_os_cmp( o1->data.enumval.enum_value.os.data, o1->data.enumval.enum_value.os.len, + return fd_os_cmp( o1->data.enumval.enum_value.os.data, o1->data.enumval.enum_value.os.len, o2->data.enumval.enum_value.os.data, o2->data.enumval.enum_value.os.len); - + case AVP_TYPE_INTEGER32: return ORDER_scalar( o1->data.enumval.enum_value.i32, o2->data.enumval.enum_value.i32 ); @@ -505,7 +419,7 @@ static int order_enum_by_val ( struct dict_object *o1, struct dict_object *o2 ) static int order_avp_by_code ( struct dict_object *o1, struct dict_object *o2 ) { TRACE_ENTRY("%p %p", o1, o2); - + return ORDER_scalar( o1->data.avp.avp_code, o2->data.avp.avp_code ); } @@ -513,7 +427,7 @@ static int order_avp_by_code ( struct dict_object *o1, struct dict_object *o2 ) static int order_avp_by_name ( struct dict_object *o1, struct dict_object *o2 ) { TRACE_ENTRY("%p %p", o1, o2); - + return fd_os_cmp( o1->data.avp.avp_name, o1->datastr_len, o2->data.avp.avp_name, o2->datastr_len ); } @@ -521,7 +435,7 @@ static int order_avp_by_name ( struct dict_object *o1, struct dict_object *o2 ) static int order_cmd_by_name ( struct dict_object *o1, struct dict_object *o2 ) { TRACE_ENTRY("%p %p", o1, o2); - + return fd_os_cmp( o1->data.cmd.cmd_name, o1->datastr_len, o2->data.cmd.cmd_name, o2->datastr_len ); } @@ -530,28 +444,28 @@ static int order_cmd_by_codefl( struct dict_object *o1, struct dict_object *o2 ) { uint8_t fl1, fl2; int cmp = 0; - + TRACE_ENTRY("%p %p", o1, o2); - + cmp = ORDER_scalar( o1->data.cmd.cmd_code, o2->data.cmd.cmd_code ); - if (cmp) + if (cmp) return cmp; - + /* Same command code, we must compare the value of the 'R' flag */ fl1 = o1->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST; fl2 = o2->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST; - + /* We want requests first, so we reverse the operators here */ return ORDER_scalar(fl2, fl1); - + } /* Compare two rule object by the AVP vendor & code that they refer (checks already performed) */ static int order_rule_by_avpvc ( struct dict_object *o1, struct dict_object *o2 ) { TRACE_ENTRY("%p %p", o1, o2); - - return ORDER_scalar(o1->data.rule.rule_avp->data.avp.avp_vendor, o2->data.rule.rule_avp->data.avp.avp_vendor) + + return ORDER_scalar(o1->data.rule.rule_avp->data.avp.avp_vendor, o2->data.rule.rule_avp->data.avp.avp_vendor) ?: ORDER_scalar(o1->data.rule.rule_avp->data.avp.avp_code, o2->data.rule.rule_avp->data.avp.avp_code) ; } @@ -737,30 +651,30 @@ static int search_vendor ( struct dictionary * dict, int criteria, const void * { int ret = 0; vendor_id_t id; - + TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result); - + switch (criteria) { case VENDOR_BY_ID: id = *(vendor_id_t *) what; SEARCH_scalar( id, &dict->dict_vendors.list[0], vendor.vendor_id, 1, &dict->dict_vendors ); break; - + case VENDOR_BY_NAME: /* "what" is a vendor name */ SEARCH_os0( what, &dict->dict_vendors.list[0], vendor.vendor_name, 0); break; - + case VENDOR_OF_APPLICATION: /* "what" should be an application object */ SEARCH_childs_parent( DICT_APPLICATION, &dict->dict_vendors ); break; - + case VENDOR_OF_AVP: /* "what" should be an avp object */ SEARCH_sentinel( DICT_AVP, 0, 1 ); break; - + default: /* Invalid criteria */ CHECK_PARAMS( criteria = 0 ); @@ -773,31 +687,31 @@ static int search_application ( struct dictionary * dict, int criteria, const vo { int ret = 0; application_id_t id; - + TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result); - + switch (criteria) { case APPLICATION_BY_ID: id = *(application_id_t *) what; SEARCH_scalar( id, &dict->dict_applications.list[0], application.application_id, 1, &dict->dict_applications ); break; - + case APPLICATION_BY_NAME: /* "what" is an application name */ SEARCH_os0( what, &dict->dict_applications.list[0], application.application_name, 0); break; - + case APPLICATION_OF_TYPE: /* "what" should be a type object */ SEARCH_childs_parent( DICT_TYPE, &dict->dict_applications ); break; - + case APPLICATION_OF_COMMAND: /* "what" should be a command object */ SEARCH_childs_parent( DICT_COMMAND, &dict->dict_applications ); break; - + default: /* Invalid criteria */ CHECK_PARAMS( criteria = 0 ); @@ -809,26 +723,26 @@ end: static int search_type ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result ) { int ret = 0; - + TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result); - + switch (criteria) { case TYPE_BY_NAME: /* "what" is a type name */ SEARCH_os0( what, &dict->dict_types, type.type_name, 1); break; - + case TYPE_OF_ENUMVAL: /* "what" should be a type_enum object */ SEARCH_childs_parent( DICT_ENUMVAL, NULL ); break; - + case TYPE_OF_AVP: /* "what" should be an avp object */ SEARCH_childs_parent( DICT_AVP, NULL ); break; - - + + default: /* Invalid criteria */ CHECK_PARAMS( criteria = 0 ); @@ -840,17 +754,17 @@ end: static int search_enumval ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result ) { int ret = 0; - + TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result); - + switch (criteria) { case ENUMVAL_BY_STRUCT: { struct dict_object * parent = NULL; struct dict_enumval_request * _what = (struct dict_enumval_request *) what; - + CHECK_PARAMS( _what && ( _what->type_obj || _what->type_name ) ); - + if (_what->type_obj != NULL) { parent = _what->type_obj; CHECK_PARAMS( verify_object(parent) && (parent->type == DICT_TYPE) ); @@ -859,9 +773,9 @@ static int search_enumval ( struct dictionary * dict, int criteria, const void * CHECK_FCT_DO( search_type( dict, TYPE_BY_NAME, _what->type_name, &parent ), CHECK_PARAMS( 0 ) ); } - + /* From here the "parent" object is valid */ - + if ( _what->search.enum_name != NULL ) { /* We are looking for this string */ SEARCH_os0( _what->search.enum_name, &parent->list[1], enumval.enum_name, 1 ); @@ -869,10 +783,10 @@ static int search_enumval ( struct dictionary * dict, int criteria, const void * /* We are looking for the value in enum_value */ switch (parent->data.type.type_base) { case AVP_TYPE_OCTETSTRING: - SEARCH_os( _what->search.enum_value.os.data, - _what->search.enum_value.os.len, - &parent->list[2], - enumval.enum_value.os , + SEARCH_os( _what->search.enum_value.os.data, + _what->search.enum_value.os.len, + &parent->list[2], + enumval.enum_value.os , 1 ); break; @@ -883,7 +797,7 @@ static int search_enumval ( struct dictionary * dict, int criteria, const void * 1, (struct dict_object *)NULL); break; - + case AVP_TYPE_INTEGER64: SEARCH_scalar( _what->search.enum_value.i64, &parent->list[2], @@ -891,7 +805,7 @@ static int search_enumval ( struct dictionary * dict, int criteria, const void * 1, (struct dict_object *)NULL); break; - + case AVP_TYPE_UNSIGNED32: SEARCH_scalar( _what->search.enum_value.u32, &parent->list[2], @@ -899,7 +813,7 @@ static int search_enumval ( struct dictionary * dict, int criteria, const void * 1, (struct dict_object *)NULL); break; - + case AVP_TYPE_UNSIGNED64: SEARCH_scalar( _what->search.enum_value.u64, &parent->list[2], @@ -907,7 +821,7 @@ static int search_enumval ( struct dictionary * dict, int criteria, const void * 1, (struct dict_object *)NULL); break; - + case AVP_TYPE_FLOAT32: SEARCH_scalar( _what->search.enum_value.f32, &parent->list[2], @@ -915,7 +829,7 @@ static int search_enumval ( struct dictionary * dict, int criteria, const void * 1, (struct dict_object *)NULL); break; - + case AVP_TYPE_FLOAT64: SEARCH_scalar( _what->search.enum_value.f64, &parent->list[2], @@ -923,17 +837,17 @@ static int search_enumval ( struct dictionary * dict, int criteria, const void * 1, (struct dict_object *)NULL); break; - + default: /* Invalid parent type basetype */ CHECK_PARAMS( parent = NULL ); } } - + } break; - - + + default: /* Invalid criteria */ CHECK_PARAMS( criteria = 0 ); @@ -945,9 +859,9 @@ end: static int search_avp ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result ) { int ret = 0; - + TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result); - + switch (criteria) { case AVP_BY_CODE: { @@ -957,20 +871,20 @@ static int search_avp ( struct dictionary * dict, int criteria, const void * wha SEARCH_scalar( code, &dict->dict_vendors.list[1], avp.avp_code, 1, (struct dict_object *)NULL ); } break; - + case AVP_BY_NAME: /* "what" is the AVP name, vendor 0 */ SEARCH_os0( what, &dict->dict_vendors.list[2], avp.avp_name, 1); break; - + case AVP_BY_CODE_AND_VENDOR: case AVP_BY_NAME_AND_VENDOR: { struct dict_avp_request * _what = (struct dict_avp_request *) what; struct dict_object * vendor = NULL; - + CHECK_PARAMS( (criteria != AVP_BY_NAME_AND_VENDOR) || _what->avp_name ); - + /* Now look for the vendor first */ CHECK_FCT( search_vendor( dict, VENDOR_BY_ID, &_what->avp_vendor, &vendor ) ); if (vendor == NULL) { @@ -980,7 +894,7 @@ static int search_avp ( struct dictionary * dict, int criteria, const void * wha ret = ENOENT; goto end; } - + /* We now have our vendor = head of the appropriate avp list */ if (criteria == AVP_BY_NAME_AND_VENDOR) { SEARCH_os0( _what->avp_name, &vendor->list[2], avp.avp_name, 1); @@ -990,15 +904,15 @@ static int search_avp ( struct dictionary * dict, int criteria, const void * wha } } break; - + case AVP_BY_STRUCT: { struct dict_avp_request_ex * _what = (struct dict_avp_request_ex *) what; struct dict_object * vendor = NULL; - + CHECK_PARAMS( _what->avp_vendor.vendor || _what->avp_vendor.vendor_id || _what->avp_vendor.vendor_name ); CHECK_PARAMS( _what->avp_data.avp_code || _what->avp_data.avp_name ); - + /* Now look for the vendor first */ if (_what->avp_vendor.vendor) { CHECK_PARAMS( ! _what->avp_vendor.vendor_id && ! _what->avp_vendor.vendor_name ); @@ -1009,7 +923,7 @@ static int search_avp ( struct dictionary * dict, int criteria, const void * wha } else { CHECK_FCT( search_vendor( dict, VENDOR_BY_NAME, _what->avp_vendor.vendor_name, &vendor ) ); } - + if (vendor == NULL) { if (result) *result = NULL; @@ -1017,7 +931,7 @@ static int search_avp ( struct dictionary * dict, int criteria, const void * wha ret = ENOENT; goto end; } - + /* We now have our vendor = head of the appropriate avp list */ if (_what->avp_data.avp_code) { CHECK_PARAMS( ! _what->avp_data.avp_name ); @@ -1027,22 +941,22 @@ static int search_avp ( struct dictionary * dict, int criteria, const void * wha } } break; - + case AVP_BY_NAME_ALL_VENDORS: { struct fd_list * li; size_t wl = strlen((char *)what); - + /* First, search for vendor 0 */ SEARCH_os0_l( what, wl, &dict->dict_vendors.list[2], avp.avp_name, 1); - + /* If not found, loop for all vendors, until found */ for (li = dict->dict_vendors.list[0].next; li != &dict->dict_vendors.list[0]; li = li->next) { SEARCH_os0_l( what, wl, &_O(li->o)->list[2], avp.avp_name, 1); } } break; - + default: /* Invalid criteria */ CHECK_PARAMS( criteria = 0 ); @@ -1054,45 +968,45 @@ end: static int search_cmd ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result ) { int ret = 0; - + TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result); - + switch (criteria) { case CMD_BY_NAME: /* "what" is a command name */ SEARCH_os0( what, &dict->dict_cmd_name, cmd.cmd_name, 1); break; - + case CMD_BY_CODE_R: case CMD_BY_CODE_A: { command_code_t code; uint8_t searchfl = 0; - + /* The command code that we are searching */ code = *(command_code_t *) what; - + /* The flag (request or answer) of the command we are searching */ if (criteria == CMD_BY_CODE_R) { searchfl = CMD_FLAG_REQUEST; } - + /* perform the search */ SEARCH_codefl( code, searchfl, &dict->dict_cmd_code ); } break; - + case CMD_ANSWER: { /* "what" is a command object of type "request" */ struct dict_object * req = (struct dict_object *) what; struct dict_object * ans = NULL; - - CHECK_PARAMS( verify_object(req) + + CHECK_PARAMS( verify_object(req) && (req->type == DICT_COMMAND) && (req->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST) && (req->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST) ); - + /* The answer is supposed to be the next element in the list, if it exists */ ans = req->list[1].next->o; if ( ans == NULL ) { @@ -1100,7 +1014,7 @@ static int search_cmd ( struct dictionary * dict, int criteria, const void * wha ret = ENOENT; goto end; } - + /* Now check that the ans element is really the correct one */ if ( (ans->data.cmd.cmd_code != req->data.cmd.cmd_code) || (!(ans->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST)) @@ -1109,13 +1023,13 @@ static int search_cmd ( struct dictionary * dict, int criteria, const void * wha ret = ENOENT; goto end; } - + if (result) *result = ans; ret = 0; - } + } break; - + default: /* Invalid criteria */ CHECK_PARAMS( criteria = 0 ); @@ -1127,32 +1041,32 @@ end: static int search_rule ( struct dictionary * dict, int criteria, const void * what, struct dict_object **result ) { int ret = 0; - + TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result); - + switch (criteria) { case RULE_BY_AVP_AND_PARENT: { struct dict_object * parent = NULL; struct dict_object * avp = NULL; struct dict_rule_request * _what = (struct dict_rule_request *) what; - - CHECK_PARAMS( _what + + CHECK_PARAMS( _what && (parent = _what->rule_parent) && (avp = _what->rule_avp ) ); - - CHECK_PARAMS( verify_object(parent) - && ((parent->type == DICT_COMMAND) + + CHECK_PARAMS( verify_object(parent) + && ((parent->type == DICT_COMMAND) || ((parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED))) ); - + CHECK_PARAMS( verify_object(avp) && (avp->type == DICT_AVP) ); - + /* Perform the search */ SEARCH_ruleavpname( avp->data.avp.avp_name, avp->datastr_len, &parent->list[2]); - + } break; - + default: /* Invalid criteria */ CHECK_PARAMS( criteria = 0 ); @@ -1172,7 +1086,7 @@ end: static DECLARE_FD_DUMP_PROTOTYPE(dump_vendor_data, void * data ) { struct dict_vendor_data * vendor = (struct dict_vendor_data *)data; - + return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: %-6u \"%s\"", vendor->vendor_id, vendor->vendor_name); } static DECLARE_FD_DUMP_PROTOTYPE(dump_application_data, void * data ) @@ -1183,9 +1097,9 @@ static DECLARE_FD_DUMP_PROTOTYPE(dump_application_data, void * data ) static DECLARE_FD_DUMP_PROTOTYPE(dump_type_data, void * data ) { struct dict_type_data * type = ( struct dict_type_data * ) data; - - return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: %-12s \"%s\"", - type_base_name[type->type_base], + + return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: %-12s \"%s\"", + type_base_name[type->type_base], type->type_name); } static DECLARE_FD_DUMP_PROTOTYPE(dump_enumval_data, struct dict_enumval_data * enumval, enum dict_avp_basetype type ) @@ -1204,7 +1118,7 @@ static DECLARE_FD_DUMP_PROTOTYPE(dump_enumval_data, struct dict_enumval_data * e CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "..."), return NULL); } break; - + case AVP_TYPE_INTEGER32: CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "%i", enumval->enum_value.i32), return NULL); break; @@ -1228,7 +1142,7 @@ static DECLARE_FD_DUMP_PROTOTYPE(dump_enumval_data, struct dict_enumval_data * e case AVP_TYPE_FLOAT64: CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "%g", enumval->enum_value.f64), return NULL); break; - + default: CHECK_MALLOC_DO(fd_dump_extend( FD_DUMP_STD_PARAMS, "??? (ERROR unknown type %d)", type), return NULL); } @@ -1237,26 +1151,26 @@ static DECLARE_FD_DUMP_PROTOTYPE(dump_enumval_data, struct dict_enumval_data * e static DECLARE_FD_DUMP_PROTOTYPE(dump_avp_data, void * data ) { struct dict_avp_data * avp = (struct dict_avp_data * ) data; - return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: v/m:" DUMP_AVPFL_str "/" DUMP_AVPFL_str ", %12s, %-6u \"%s\"", - DUMP_AVPFL_val(avp->avp_flag_val), - DUMP_AVPFL_val(avp->avp_flag_mask), - type_base_name[avp->avp_basetype], - avp->avp_code, + return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: v/m:" DUMP_AVPFL_str "/" DUMP_AVPFL_str ", %12s, %-6u \"%s\"", + DUMP_AVPFL_val(avp->avp_flag_val), + DUMP_AVPFL_val(avp->avp_flag_mask), + type_base_name[avp->avp_basetype], + avp->avp_code, avp->avp_name ); } static DECLARE_FD_DUMP_PROTOTYPE(dump_command_data, void * data ) { struct dict_cmd_data * cmd = (struct dict_cmd_data *) data; - return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: v/m:" DUMP_CMDFL_str "/" DUMP_CMDFL_str ", %-6u \"%s\"", + return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: v/m:" DUMP_CMDFL_str "/" DUMP_CMDFL_str ", %-6u \"%s\"", DUMP_CMDFL_val(cmd->cmd_flag_val), DUMP_CMDFL_val(cmd->cmd_flag_mask), cmd->cmd_code, cmd->cmd_name); } static DECLARE_FD_DUMP_PROTOTYPE(dump_rule_data, void * data ) { struct dict_rule_data * rule = (struct dict_rule_data * )data; return fd_dump_extend( FD_DUMP_STD_PARAMS, "data: pos:%d ord:%d m/M:%2d/%2d avp:\"%s\"", - rule->rule_position, - rule->rule_order, - rule->rule_min, + rule->rule_position, + rule->rule_order, + rule->rule_min, rule->rule_max, rule->rule_avp->data.avp.avp_name); } @@ -1283,27 +1197,27 @@ static DECLARE_FD_DUMP_PROTOTYPE(dump_list, struct fd_list * sentinel, int paren static DECLARE_FD_DUMP_PROTOTYPE(dump_object, struct dict_object * obj, int parents, int depth, int indent ) { CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%*s{dictobj}(@%p): ", indent, "", obj), return NULL); - + if (!verify_object(obj)) { CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID/NULL"), return NULL); return *buf; } - - CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%s p:%p ", - _OBINFO(obj).name, + + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "%s p:%p ", + _OBINFO(obj).name, obj->parent), return NULL); - + if (obj->type == DICT_ENUMVAL) { CHECK_MALLOC_DO( dump_enumval_data ( FD_DUMP_STD_PARAMS, &obj->data.enumval, obj->parent->data.type.type_base ), return NULL); } else { CHECK_MALLOC_DO( _OBINFO(obj).dump_data(FD_DUMP_STD_PARAMS, &obj->data), return NULL); } - + if (parents) { CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n%*sparent:", indent + 1, ""), return NULL); CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, obj->parent, parents-1, 0, 0 ), return NULL); } - + if (depth) { int i; for (i=0; i<NB_LISTS_PER_OBJ; i++) { @@ -1313,16 +1227,16 @@ static DECLARE_FD_DUMP_PROTOTYPE(dump_object, struct dict_object * obj, int pare } } } - + return *buf; } DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump_object, struct dict_object * obj) { FD_DUMP_HANDLE_OFFSET(); - + CHECK_MALLOC_DO( dump_object(FD_DUMP_STD_PARAMS, obj, 1, 2, 0), return NULL); - + return *buf; } @@ -1330,44 +1244,44 @@ DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump, struct dictionary * dict) { int i; struct fd_list * li; - + FD_DUMP_HANDLE_OFFSET(); - + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{dictionary}(@%p): ", dict), return NULL); - + if ((dict == NULL) || (dict->dict_eyec != DICT_EYECATCHER)) { return fd_dump_extend(FD_DUMP_STD_PARAMS, "INVALID/NULL"); } - + CHECK_POSIX_DO( pthread_rwlock_rdlock( &dict->dict_lock ), /* ignore */ ); - - CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict(%p) : VENDORS / AVP / RULES}\n", dict), goto error); + + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict}(@%p): VENDORS / AVP / RULES\n", dict), goto error); CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, &dict->dict_vendors, 0, 3, 3 ), goto error); for (li = dict->dict_vendors.list[0].next; li != &dict->dict_vendors.list[0]; li = li->next) { CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL); CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, li->o, 0, 3, 3 ), goto error); } - - CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict(%p) : APPLICATIONS}\n", dict), goto error); + + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict}(@%p): APPLICATIONS\n", dict), goto error); CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, &dict->dict_applications, 0, 1, 3 ), goto error); for (li = dict->dict_applications.list[0].next; li != &dict->dict_applications.list[0]; li = li->next) { CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL); CHECK_MALLOC_DO( dump_object (FD_DUMP_STD_PARAMS, li->o, 0, 1, 3 ), goto error); } - - CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict(%p) : TYPES / ENUMVAL}", dict), goto error); + + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict}(@%p): TYPES / ENUMVAL", dict), goto error); CHECK_MALLOC_DO( dump_list(FD_DUMP_STD_PARAMS, &dict->dict_types, 0, 2, 3 ), goto error); - - CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict(%p) : COMMANDS / RULES}", dict), goto error); + + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict}(@%p): COMMANDS / RULES", dict), goto error); CHECK_MALLOC_DO( dump_list(FD_DUMP_STD_PARAMS, &dict->dict_cmd_code, 0, 0, 3 ), goto error); - - CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict(%p) : statistics}", dict), goto error); + + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {dict}(@%p): statistics", dict), goto error); for (i=1; i<=DICT_TYPE_MAX; i++) CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n %5d: %s", dict->dict_count[i], dict_obj_info[i].name), goto error); - + CHECK_POSIX_DO( pthread_rwlock_unlock( &dict->dict_lock ), /* ignore */ ); return *buf; -error: +error: /* Free the rwlock */ CHECK_POSIX_DO( pthread_rwlock_unlock( &dict->dict_lock ), /* ignore */ ); return NULL; @@ -1379,7 +1293,7 @@ error: static DECLARE_FD_DUMP_PROTOTYPE(dump_val_os, union avp_value * value) { int i; - + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "<"), return NULL); for (i = 0; i < value->os.len; i++) { if (i == 1024) { /* Dump only up to 1024 bytes of the buffer */ @@ -1428,7 +1342,7 @@ static DECLARE_FD_DUMP_PROTOTYPE((*get_default_dump_val_cb(enum dict_avp_basetyp switch (datatype) { case AVP_TYPE_OCTETSTRING: return &dump_val_os; - + case AVP_TYPE_INTEGER32: return &dump_val_i32; @@ -1446,7 +1360,7 @@ static DECLARE_FD_DUMP_PROTOTYPE((*get_default_dump_val_cb(enum dict_avp_basetyp case AVP_TYPE_FLOAT64: return &dump_val_f64; - + case AVP_TYPE_GROUPED: TRACE_DEBUG(FULL, "error: grouped AVP with a value!"); } @@ -1460,24 +1374,24 @@ static DECLARE_FD_DUMP_PROTOTYPE((*get_default_dump_val_cb(enum dict_avp_basetyp typedef DECLARE_FD_DUMP_PROTOTYPE((*dump_val_cb_t), union avp_value *); /* Formatter for the AVP value dump line */ -static DECLARE_FD_DUMP_PROTOTYPE(dump_avp_val, union avp_value *avp_value, - dump_val_cb_t def_dump_val_cb, - dump_val_cb_t dump_val_cb, - enum dict_avp_basetype datatype, - char * type_name, - char * const_name, - int indent, +static DECLARE_FD_DUMP_PROTOTYPE(dump_avp_val, union avp_value *avp_value, + dump_val_cb_t def_dump_val_cb, + dump_val_cb_t dump_val_cb, + enum dict_avp_basetype datatype, + char * type_name, + char * const_name, + int indent, int header) { if (header) { /* Header for all AVP values dumps: */ CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, INOBJHDR "value ", INOBJHDRVAL), return NULL); - + /* If the type is provided, write it */ if (type_name) { CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "t: '%s' ", type_name), return NULL); } - + /* Always give the base datatype anyway */ CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(%s) ", type_base_name[datatype]), return NULL); @@ -1495,7 +1409,7 @@ static DECLARE_FD_DUMP_PROTOTYPE(dump_avp_val, union avp_value *avp_value, if (const_name) { CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, ")"), return NULL); } - + /* Done! */ return *buf; } @@ -1507,9 +1421,9 @@ DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump_avp_value, union avp_value *avp_value, st struct dict_object * type = NULL; char * type_name = NULL; char * const_name = NULL; - + FD_DUMP_HANDLE_OFFSET(); - + /* Handle invalid parameters */ if (!avp_value) { CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(avp value not set)"), return NULL); @@ -1520,24 +1434,24 @@ DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump_avp_value, union avp_value *avp_value, st CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(model not set)"), return NULL); return *buf; } - + if (! ( verify_object(model) && (model->type == DICT_AVP) )) { CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "(invalid model)"), return NULL); return *buf; } - + /* Get the type definition of this AVP */ type = model->parent; if (type) { struct dict_enumval_request request; struct dict_object * enumval = NULL; - + type_name = type->data.type.type_name; - + /* overwrite the dump function ? */ if (type->data.type.type_dump) dump_val_cb = type->data.type.type_dump; - + /* Now check if the AVP value matches a constant */ memset(&request, 0, sizeof(request)); request.type_obj = type; @@ -1548,7 +1462,7 @@ DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump_avp_value, union avp_value *avp_value, st const_name = enumval->data.enumval.enum_name; } } - + /* And finally, dump the value */ CHECK_MALLOC_DO( dump_avp_val(FD_DUMP_STD_PARAMS, avp_value, get_default_dump_val_cb(model->data.avp.avp_basetype), dump_val_cb, model->data.avp.avp_basetype, type_name, const_name, indent, header), return NULL ); return *buf; @@ -1568,9 +1482,9 @@ DECLARE_FD_DUMP_PROTOTYPE(fd_dict_dump_avp_value, union avp_value *avp_value, st int fd_dict_gettype ( struct dict_object * object, enum dict_object_type * type) { TRACE_ENTRY("%p %p", object, type); - + CHECK_PARAMS( type && verify_object(object) ); - + /* Copy the value and return */ *type = object->type; return 0; @@ -1579,9 +1493,9 @@ int fd_dict_gettype ( struct dict_object * object, enum dict_object_type * type) int fd_dict_getdict ( struct dict_object * object, struct dictionary ** dict) { TRACE_ENTRY("%p %p", object, dict); - + CHECK_PARAMS( dict && verify_object(object) ); - + /* Copy the value and return */ *dict = object->dico; return 0; @@ -1592,9 +1506,9 @@ int fd_dict_getdict ( struct dict_object * object, struct dictionary ** dict) int fd_dict_getval ( struct dict_object * object, void * val) { TRACE_ENTRY("%p %p", object, val); - + CHECK_PARAMS( val && verify_object(object) ); - + /* Copy the value and return */ memcpy(val, &object->data, _OBINFO(object).datasize);; return 0; @@ -1604,57 +1518,64 @@ int fd_dict_getval ( struct dict_object * object, void * val) int fd_dict_new ( struct dictionary * dict, enum dict_object_type type, void * data, struct dict_object * parent, struct dict_object **ref ) { int ret = 0; + int dupos = 0; struct dict_object * new = NULL; struct dict_object * vendor = NULL; struct dict_object * locref = NULL; - + TRACE_ENTRY("%p %d(%s) %p %p %p", dict, type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, data, parent, ref); - + /* Check parameters */ CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && CHECK_TYPE(type) && data ); - + /* Check the "parent" parameter */ switch (dict_obj_info[type].parent) { case 0: /* parent is forbidden */ CHECK_PARAMS_DO( parent == NULL, goto error_param ); - + case 1: /* parent is optional */ if (parent == NULL) break; - + case 2: /* parent is mandatory */ CHECK_PARAMS_DO( verify_object(parent), goto error_param ); - + if (type == DICT_RULE ) { /* Special case : grouped AVP or Command parents are allowed */ - CHECK_PARAMS_DO( (parent->type == DICT_COMMAND ) + CHECK_PARAMS_DO( (parent->type == DICT_COMMAND ) || ( (parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED ) ), goto error_param ); } else { CHECK_PARAMS_DO( parent->type == dict_obj_info[type].parenttype, goto error_param ); } } - + /* For AVP object, we must also check that the "vendor" referenced exists */ if (type == DICT_AVP) { CHECK_FCT_DO( fd_dict_search( dict, DICT_VENDOR, VENDOR_BY_ID, &(((struct dict_avp_data *)data)->avp_vendor), (void*)&vendor, ENOENT ), { TRACE_DEBUG(INFO, "Unable to find vendor '%d' referenced in the AVP data", ((struct dict_avp_data *)data)->avp_vendor); goto error_param; } ); - + /* Also check if a parent is provided, that the type are the same */ if (parent) { CHECK_PARAMS_DO( parent->data.type.type_base == ((struct dict_avp_data *)data)->avp_basetype, goto error_param ); } } - + /* For RULE object, we must also check that the "avp" referenced exists */ if (type == DICT_RULE) { CHECK_PARAMS_DO( verify_object(((struct dict_rule_data *)data)->rule_avp), goto error_param ); CHECK_PARAMS_DO( ((struct dict_rule_data *)data)->rule_avp->type == DICT_AVP, goto error_param ); } - + /* For COMMAND object, check that the 'R' flag is fixed */ if (type == DICT_COMMAND) { CHECK_PARAMS_DO( ((struct dict_cmd_data *)data)->cmd_flag_mask & CMD_FLAG_REQUEST, goto error_param ); } - + + /* For ENUMVAL object, check if the parent type is an OctetString */ + if (type == DICT_ENUMVAL) { + if (parent->data.type.type_base == AVP_TYPE_OCTETSTRING) + dupos = 1; + } + /* We have to check that the new values are not equal to the sentinels */ if (type == DICT_VENDOR) { CHECK_PARAMS_DO( ((struct dict_vendor_data *)data)->vendor_id != 0, goto error_param ); @@ -1662,19 +1583,19 @@ int fd_dict_new ( struct dictionary * dict, enum dict_object_type type, void * d if (type == DICT_APPLICATION) { CHECK_PARAMS_DO( ((struct dict_application_data *)data)->application_id != 0, goto error_param ); } - + /* Parameters are valid, create the new object */ CHECK_MALLOC( new = malloc(sizeof(struct dict_object)) ); - + /* Initialize the data of the new object */ init_object(new, type); - init_object_data(new, data, type); + init_object_data(new, data, type, dupos); new->dico = dict; new->parent = parent; - + /* We will change the dictionary => acquire the write lock */ CHECK_POSIX_DO( ret = pthread_rwlock_wrlock(&dict->dict_lock), goto error_free ); - + /* Now link the object -- this also checks that no object with same keys already exists */ switch (type) { case DICT_VENDOR: @@ -1683,83 +1604,83 @@ int fd_dict_new ( struct dictionary * dict, enum dict_object_type type, void * d if (ret) goto error_unlock; break; - + case DICT_APPLICATION: /* An application object is linked in the g_dict_applciations.list[0], by their id */ ret = fd_list_insert_ordered ( &dict->dict_applications.list[0], &new->list[0], (int (*)(void*, void *))order_appli_by_id, (void **)&locref ); if (ret) goto error_unlock; break; - + case DICT_TYPE: /* A type object is linked in g_list_types by its name */ ret = fd_list_insert_ordered ( &dict->dict_types, &new->list[0], (int (*)(void*, void *))order_type_by_name, (void **)&locref ); if (ret) goto error_unlock; break; - + case DICT_ENUMVAL: /* A type_enum object is linked in it's parent 'type' object lists 1 and 2 by its name and values */ ret = fd_list_insert_ordered ( &parent->list[1], &new->list[0], (int (*)(void*, void *))order_enum_by_name, (void **)&locref ); if (ret) goto error_unlock; - + ret = fd_list_insert_ordered ( &parent->list[2], &new->list[1], (int (*)(void*, void *))order_enum_by_val, (void **)&locref ); - if (ret) { - fd_list_unlink(&new->list[0]); - goto error_unlock; + if (ret) { + fd_list_unlink(&new->list[0]); + goto error_unlock; } break; - + case DICT_AVP: /* An avp object is linked in lists 1 and 2 of its vendor, by code and name */ ret = fd_list_insert_ordered ( &vendor->list[1], &new->list[0], (int (*)(void*, void *))order_avp_by_code, (void **)&locref ); if (ret) goto error_unlock; - + ret = fd_list_insert_ordered ( &vendor->list[2], &new->list[1], (int (*)(void*, void *))order_avp_by_name, (void **)&locref ); if (ret) { fd_list_unlink(&new->list[0]); goto error_unlock; } break; - + case DICT_COMMAND: /* A command object is linked in g_list_cmd_name and g_list_cmd_code by its name and code */ ret = fd_list_insert_ordered ( &dict->dict_cmd_code, &new->list[1], (int (*)(void*, void *))order_cmd_by_codefl, (void **)&locref ); if (ret) goto error_unlock; - + ret = fd_list_insert_ordered ( &dict->dict_cmd_name, &new->list[0], (int (*)(void*, void *))order_cmd_by_name, (void **)&locref ); if (ret) { fd_list_unlink(&new->list[1]); goto error_unlock; } break; - + case DICT_RULE: /* A rule object is linked in list[2] of its parent command or AVP by the name of the AVP it refers */ ret = fd_list_insert_ordered ( &parent->list[2], &new->list[0], (int (*)(void*, void *))order_rule_by_avpvc, (void **)&locref ); if (ret) goto error_unlock; break; - + default: ASSERT(0); } - + /* A new object has been created, increment the global counter */ dict->dict_count[type]++; - + /* Unlock the dictionary */ CHECK_POSIX_DO( ret = pthread_rwlock_unlock(&dict->dict_lock), goto error_free ); - + /* Save the pointer to the new object */ if (ref) *ref = new; - + return 0; - + error_param: ret = EINVAL; goto all_errors; @@ -1772,19 +1693,19 @@ error_unlock: case DICT_VENDOR: TRACE_DEBUG(FULL, "Vendor %s already in dictionary", new->data.vendor.vendor_name); /* if we are here, it means the two vendors id are identical */ - if (fd_os_cmp(locref->data.vendor.vendor_name, locref->datastr_len, + if (fd_os_cmp(locref->data.vendor.vendor_name, locref->datastr_len, new->data.vendor.vendor_name, new->datastr_len)) { TRACE_DEBUG(INFO, "Conflicting vendor name: %s", new->data.vendor.vendor_name); break; } /* Otherwise (same name), we consider the function succeeded, since the (same) object is in the dictionary */ - ret = 0; + ret = 0; break; case DICT_APPLICATION: TRACE_DEBUG(FULL, "Application %s already in dictionary", new->data.application.application_name); /* got same id */ - if (fd_os_cmp(locref->data.application.application_name, locref->datastr_len, + if (fd_os_cmp(locref->data.application.application_name, locref->datastr_len, new->data.application.application_name, new->datastr_len)) { TRACE_DEBUG(FULL, "Conflicting application name"); break; @@ -1922,15 +1843,15 @@ all_errors: if (ret != 0) { char * buf = NULL; size_t len = 0, offset=0; - + if (type == DICT_ENUMVAL) { CHECK_MALLOC( dump_enumval_data ( &buf, &len, &offset, data, parent->data.type.type_base )); } else { CHECK_MALLOC( dict_obj_info[CHECK_TYPE(type) ? type : 0].dump_data(&buf, &len, &offset, data) ); } - + TRACE_DEBUG(INFO, "An error occurred while adding the following data in the dictionary: %s", buf); - + if (ret == EEXIST) { offset=0; CHECK_MALLOC( dump_object(&buf, &len, &offset, locref, 0, 0, 0) ); @@ -1949,14 +1870,14 @@ int fd_dict_delete(struct dict_object * obj) int i; struct dictionary * dict; int ret=0; - + /* check params */ CHECK_PARAMS( verify_object(obj) && obj->dico); dict = obj->dico; /* Lock the dictionary for change */ CHECK_POSIX( pthread_rwlock_wrlock(&dict->dict_lock) ); - + /* check the object is not sentinel for another list */ for (i=0; i<NB_LISTS_PER_OBJ; i++) { if (!_OBINFO(obj).haslist[i] && !(FD_IS_LIST_EMPTY(&obj->list[i]))) { @@ -1969,14 +1890,14 @@ int fd_dict_delete(struct dict_object * obj) break; } } - + /* ok, now destroy the object */ if (!ret) destroy_object(obj); - + /* Unlock */ CHECK_POSIX( pthread_rwlock_unlock(&dict->dict_lock) ); - + return ret; } @@ -1984,25 +1905,25 @@ int fd_dict_delete(struct dict_object * obj) int fd_dict_search ( struct dictionary * dict, enum dict_object_type type, int criteria, const void * what, struct dict_object **result, int retval ) { int ret = 0; - + TRACE_ENTRY("%p %d(%s) %d %p %p %d", dict, type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, criteria, what, result, retval); - + /* Check param */ CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && CHECK_TYPE(type) ); - + /* Lock the dictionary for reading */ CHECK_POSIX( pthread_rwlock_rdlock(&dict->dict_lock) ); - + /* Now call the type-specific search function */ ret = dict_obj_info[type].search_fct (dict, criteria, what, result); - + /* Unlock */ CHECK_POSIX( pthread_rwlock_unlock(&dict->dict_lock) ); - + /* Update the return value as needed */ if ((result != NULL) && (*result == NULL)) ret = retval; - + return ret; } @@ -2015,14 +1936,14 @@ All returned list must be accessed like this: ... } -The following criteria are allowed, with corresponding parent. +The following criteria are allowed, with corresponding parent. The parent is either struct dictionary * or struct dict_object * - + VENDOR_BY_ID : (parent = dictionary) returns list of vendors ordered by ID APPLICATION_BY_ID : (parent = dictionary) returns list of applications ordered by ID - ** for these two lists, the Vendor with id 0 and applciation with id 0 are excluded. + ** for these two lists, the Vendor with id 0 and applciation with id 0 are excluded. You must resolve them separatly with dict_search. - + TYPE_BY_NAME : (parent = dictionary) returns list of types ordered by name (osstring order) ENUMVAL_BY_NAME : (parent = type object) return list of constants for this type ordered by name (osstring order) ENUMVAL_BY_VALUE : (parent = type object) return list of constants for this type ordered by values @@ -2038,69 +1959,69 @@ int fd_dict_getlistof(int criteria, void * parent, struct fd_list ** sentinel) { struct dictionary * dict = parent; struct dict_object * obj_parent = parent; - + TRACE_ENTRY("%i %p %p", criteria, parent, sentinel); - + CHECK_PARAMS(sentinel && parent); - + switch(criteria) { case VENDOR_BY_ID: /* parent must be the dictionary */ CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER); *sentinel = &dict->dict_vendors.list[0]; break; - + case APPLICATION_BY_ID: /* parent must be the dictionary */ CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER); *sentinel = &dict->dict_applications.list[0]; break; - + case TYPE_BY_NAME: /* parent must be the dictionary */ CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER); *sentinel = &dict->dict_types; break; - + case ENUMVAL_BY_NAME: /* parent must be a type object */ CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_TYPE)); *sentinel = &obj_parent->list[1]; break; - + case ENUMVAL_BY_VALUE: /* parent must be a type object */ CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_TYPE)); *sentinel = &obj_parent->list[2]; break; - + case AVP_BY_NAME: /* parent must be a VENDOR object */ CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_VENDOR)); *sentinel = &obj_parent->list[2]; break; - + case AVP_BY_CODE: /* parent must be a VENDOR object */ CHECK_PARAMS(verify_object(obj_parent) && (obj_parent->type == DICT_VENDOR)); *sentinel = &obj_parent->list[1]; break; - + case CMD_BY_NAME: /* parent must be the dictionary */ CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER); *sentinel = &dict->dict_cmd_name; break; - + case CMD_BY_CODE_R: /* parent must be the dictionary */ CHECK_PARAMS(dict->dict_eyec == DICT_EYECATCHER); *sentinel = &dict->dict_cmd_code; break; - + case RULE_BY_AVP_AND_PARENT: /* parent must be command or grouped AVP */ CHECK_PARAMS(verify_object(obj_parent)); CHECK_PARAMS( (obj_parent->type == DICT_COMMAND) || - ((obj_parent->type == DICT_AVP) + ((obj_parent->type == DICT_AVP) && (obj_parent->data.avp.avp_basetype == AVP_TYPE_GROUPED)) ); *sentinel = &obj_parent->list[2]; break; - + default: CHECK_PARAMS(0); } - + return 0; } @@ -2116,23 +2037,23 @@ int fd_dict_getlistof(int criteria, void * parent, struct fd_list ** sentinel) int fd_dict_init ( struct dictionary ** dict) { struct dictionary * new = NULL; - + TRACE_ENTRY("%p", dict); - + /* Sanity checks */ ASSERT( (sizeof(type_base_name) / sizeof(type_base_name[0])) == (AVP_TYPE_MAX + 1) ); ASSERT( (sizeof(dict_obj_info) / sizeof(dict_obj_info[0])) == (DICT_TYPE_MAX + 1) ); CHECK_PARAMS(dict); - + /* Allocate the memory for the dictionary */ CHECK_MALLOC( new = malloc(sizeof(struct dictionary)) ); memset(new, 0, sizeof(struct dictionary)); - + new->dict_eyec = DICT_EYECATCHER; - + /* Initialize the lock for the dictionary */ CHECK_POSIX( pthread_rwlock_init(&new->dict_lock, NULL) ); - + /* Initialize the sentinel for vendors and AVP lists */ init_object( &new->dict_vendors, DICT_VENDOR ); #define NO_VENDOR_NAME "(no vendor)" @@ -2140,7 +2061,7 @@ int fd_dict_init ( struct dictionary ** dict) new->dict_vendors.datastr_len = CONSTSTRLEN(NO_VENDOR_NAME); /* new->dict_vendors.list[0].o = NULL; *//* overwrite since element is also sentinel for this list. */ new->dict_vendors.dico = new; - + /* Initialize the sentinel for applications */ init_object( &new->dict_applications, DICT_APPLICATION ); #define APPLICATION_0_NAME "Diameter Common Messages" @@ -2148,14 +2069,14 @@ int fd_dict_init ( struct dictionary ** dict) new->dict_applications.datastr_len = CONSTSTRLEN(APPLICATION_0_NAME); /* new->dict_applications.list[0].o = NULL; *//* overwrite since since element is also sentinel for this list. */ new->dict_applications.dico = new; - + /* Initialize the sentinel for types */ fd_list_init ( &new->dict_types, NULL ); - + /* Initialize the sentinels for commands */ fd_list_init ( &new->dict_cmd_name, NULL ); fd_list_init ( &new->dict_cmd_code, NULL ); - + /* Initialize the error command object */ init_object( &new->dict_cmd_error, DICT_COMMAND ); #define GENERIC_ERROR_NAME "(generic error format)" @@ -2164,9 +2085,9 @@ int fd_dict_init ( struct dictionary ** dict) new->dict_cmd_error.data.cmd.cmd_flag_mask=CMD_FLAG_ERROR | CMD_FLAG_REQUEST | CMD_FLAG_RETRANSMIT; new->dict_cmd_error.data.cmd.cmd_flag_val =CMD_FLAG_ERROR; new->dict_cmd_error.dico = new; - + *dict = new; - + /* Done */ return 0; } @@ -2175,13 +2096,13 @@ int fd_dict_init ( struct dictionary ** dict) int fd_dict_fini ( struct dictionary ** dict) { int i; - + TRACE_ENTRY(""); CHECK_PARAMS( dict && *dict && ((*dict)->dict_eyec == DICT_EYECATCHER) ); - + /* Acquire the write lock to make sure no other operation is ongoing */ CHECK_POSIX( pthread_rwlock_wrlock(&(*dict)->dict_lock) ); - + /* Empty all the lists, free the elements */ destroy_list ( &(*dict)->dict_cmd_error.list[2] ); destroy_list ( &(*dict)->dict_cmd_code ); @@ -2191,14 +2112,14 @@ int fd_dict_fini ( struct dictionary ** dict) destroy_list ( &(*dict)->dict_applications.list[i] ); destroy_list ( &(*dict)->dict_vendors.list[i] ); } - + /* Dictionary is empty, now destroy the lock */ CHECK_POSIX( pthread_rwlock_unlock(&(*dict)->dict_lock) ); CHECK_POSIX( pthread_rwlock_destroy(&(*dict)->dict_lock) ); - + free(*dict); *dict = NULL; - + return 0; } @@ -2215,32 +2136,32 @@ int fd_dict_iterate_rules ( struct dict_object *parent, void * data, int (*cb)(v { int ret = 0; struct fd_list * li; - + TRACE_ENTRY("%p %p %p", parent, data, cb); - + /* Check parameters */ CHECK_PARAMS( verify_object(parent) ); - CHECK_PARAMS( (parent->type == DICT_COMMAND) + CHECK_PARAMS( (parent->type == DICT_COMMAND) || ((parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED)) ); - TRACE_DEBUG (FULL, "Iterating on rules of %s: '%s'.", - _OBINFO(parent).name, - parent->type == DICT_COMMAND ? + TRACE_DEBUG (FULL, "Iterating on rules of %s: '%s'.", + _OBINFO(parent).name, + parent->type == DICT_COMMAND ? parent->data.cmd.cmd_name : parent->data.avp.avp_name); - + /* Acquire the read lock */ CHECK_POSIX( pthread_rwlock_rdlock(&parent->dico->dict_lock) ); - + /* go through the list and call the cb on each rule data */ for (li = &(parent->list[2]); li->next != &(parent->list[2]); li = li->next) { ret = (*cb)(data, &(_O(li->next->o)->data.rule)); if (ret != 0) break; } - + /* Release the lock */ CHECK_POSIX( pthread_rwlock_unlock(&parent->dico->dict_lock) ); - + return ret; } @@ -2250,25 +2171,25 @@ uint32_t * fd_dict_get_vendorid_list(struct dictionary * dict) uint32_t * ret = NULL; int i = 0; struct fd_list * li; - + TRACE_ENTRY(); - + /* Acquire the read lock */ CHECK_POSIX_DO( pthread_rwlock_rdlock(&dict->dict_lock), return NULL ); - + /* Allocate an array to contain all the elements */ CHECK_MALLOC_DO( ret = calloc( dict->dict_count[DICT_VENDOR] + 1, sizeof(uint32_t) ), goto out ); - + /* Copy the vendors IDs */ for (li = dict->dict_vendors.list[0].next; li != &(dict->dict_vendors.list[0]); li = li->next) { ret[i] = _O(li->o)->data.vendor.vendor_id; i++; ASSERT( i <= dict->dict_count[DICT_VENDOR] ); } -out: +out: /* Release the lock */ CHECK_POSIX_DO( pthread_rwlock_unlock(&dict->dict_lock), return NULL ); - + return ret; } diff --git a/libfdproto/dictionary_functions.c b/libfdproto/dictionary_functions.c index 315b417..9c45903 100644 --- a/libfdproto/dictionary_functions.c +++ b/libfdproto/dictionary_functions.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2015, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -287,7 +287,7 @@ static int time_t_to_diameter_string(time_t time_stamp, char **result) { int fd_dictfct_Time_encode(void * data, union avp_value * avp_value) { - char * buf; + char * buf = NULL; size_t len; TRACE_ENTRY("%p %p", data, avp_value); diff --git a/libfdproto/fifo.c b/libfdproto/fifo.c index cf5b7fc..4740b90 100644 --- a/libfdproto/fifo.c +++ b/libfdproto/fifo.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -50,18 +50,18 @@ /* Definition of a FIFO queue object */ struct fifo { int eyec; /* An eye catcher, also used to check a queue is valid. FIFO_EYEC */ - + pthread_mutex_t mtx; /* Mutex protecting this queue */ pthread_cond_t cond_pull; /* condition variable for pulling threads */ pthread_cond_t cond_push; /* condition variable for pushing threads */ - + struct fd_list list; /* sentinel for the list of elements */ int count; /* number of objects in the list */ int thrs; /* number of threads waiting for a new element (when count is 0) */ - + int max; /* maximum number of items to accept if not 0 */ int thrs_push; /* number of threads waitnig to push an item */ - + uint16_t high; /* High level threshold (see libfreeDiameter.h for details) */ uint16_t low; /* Low level threshhold */ void *data; /* Opaque pointer for threshold callbacks */ @@ -69,12 +69,12 @@ struct fifo { void (*l_cb)(struct fifo *, void **); int highest;/* The highest count value for which h_cb has been called */ int highest_ever; /* The max count value this queue has reached (for tweaking) */ - + long long total_items; /* Cumulated number of items that went through this fifo (excluding current count), always increasing. */ struct timespec total_time; /* Cumulated time all items spent in this queue, including blocking time (always growing, use deltas for monitoring) */ struct timespec blocking_time; /* Cumulated time threads trying to post new items were blocked (queue full). */ struct timespec last_time; /* For the last element retrieved from the queue, how long it take between posting (including blocking) and poping */ - + }; struct fifo_item { @@ -93,231 +93,252 @@ struct fifo_item { int fd_fifo_new ( struct fifo ** queue, int max ) { struct fifo * new; - + TRACE_ENTRY( "%p", queue ); - + CHECK_PARAMS( queue ); - + /* Create a new object */ CHECK_MALLOC( new = malloc (sizeof (struct fifo) ) ); - + /* Initialize the content */ memset(new, 0, sizeof(struct fifo)); - + new->eyec = FIFO_EYEC; CHECK_POSIX( pthread_mutex_init(&new->mtx, NULL) ); CHECK_POSIX( pthread_cond_init(&new->cond_pull, NULL) ); CHECK_POSIX( pthread_cond_init(&new->cond_push, NULL) ); new->max = max; - + fd_list_init(&new->list, NULL); - + /* We're done */ *queue = new; return 0; } +int fd_fifo_set_max (struct fifo * queue, int max) +{ + queue->max = max; + return 0; +} + + /* Dump the content of a queue */ DECLARE_FD_DUMP_PROTOTYPE(fd_fifo_dump, char * name, struct fifo * queue, fd_fifo_dump_item_cb dump_item) { FD_DUMP_HANDLE_OFFSET(); - + if (name) { - CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'(@%p): ", name, queue), return NULL); + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'(@%p): ", name, queue), return NULL); } else { CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{fifo}(@%p): ", queue), return NULL); } - + if (!CHECK_FIFO( queue )) { return fd_dump_extend(FD_DUMP_STD_PARAMS, "INVALID/NULL"); } - + CHECK_POSIX_DO( pthread_mutex_lock( &queue->mtx ), /* continue */ ); - CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "items:%d,%d,%d threads:%d,%d stats:%lld/%ld.%06ld,%ld.%06ld,%ld.%06ld thresholds:%d,%d,%d,%p,%p,%p", + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "items:%d,%d,%d threads:%d,%d stats:%lld/%ld.%06ld,%ld.%06ld,%ld.%06ld thresholds:%d,%d,%d,%p,%p,%p", queue->count, queue->highest_ever, queue->max, queue->thrs, queue->thrs_push, queue->total_items,(long)queue->total_time.tv_sec,(long)(queue->total_time.tv_nsec/1000),(long)queue->blocking_time.tv_sec,(long)(queue->blocking_time.tv_nsec/1000),(long)queue->last_time.tv_sec,(long)(queue->last_time.tv_nsec/1000), - queue->high, queue->low, queue->highest, queue->h_cb, queue->l_cb, queue->data), + queue->high, queue->low, queue->highest, queue->h_cb, queue->l_cb, queue->data), goto error); - + if (dump_item) { struct fd_list * li; int i = 0; for (li = queue->list.next; li != &queue->list; li = li->next) { struct fifo_item * fi = (struct fifo_item *)li; - CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n [#%i](@%p)@%ld.%06ld: ", - i++, fi->item.o, (long)fi->posted_on.tv_sec,(long)(fi->posted_on.tv_nsec/1000)), + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n [#%i](@%p)@%ld.%06ld: ", + i++, fi->item.o, (long)fi->posted_on.tv_sec,(long)(fi->posted_on.tv_nsec/1000)), goto error); CHECK_MALLOC_DO( (*dump_item)(FD_DUMP_STD_PARAMS, fi->item.o), goto error); } } CHECK_POSIX_DO( pthread_mutex_unlock( &queue->mtx ), /* continue */ ); - + return *buf; error: CHECK_POSIX_DO( pthread_mutex_unlock( &queue->mtx ), /* continue */ ); return NULL; } -/* Delete a queue. It must be empty. */ +/* Delete a queue. It must be empty. */ int fd_fifo_del ( struct fifo ** queue ) { struct fifo * q; +#ifndef NDEBUG int loops = 0; - +#endif + TRACE_ENTRY( "%p", queue ); + if (queue && *queue == NULL) { + /* Queue already (in the process of being) deleted */ + return 0; + } + CHECK_PARAMS( queue && CHECK_FIFO( *queue ) ); - + q = *queue; - + CHECK_POSIX( pthread_mutex_lock( &q->mtx ) ); - + if ((q->count != 0) || (q->data != NULL)) { TRACE_DEBUG(INFO, "The queue cannot be destroyed (%d, %p)", q->count, q->data); CHECK_POSIX_DO( pthread_mutex_unlock( &q->mtx ), /* no fallback */ ); return EINVAL; } - + /* Ok, now invalidate the queue */ q->eyec = 0xdead; - + /* Have all waiting threads return an error */ while (q->thrs) { CHECK_POSIX( pthread_mutex_unlock( &q->mtx )); CHECK_POSIX( pthread_cond_signal(&q->cond_pull) ); usleep(1000); - + CHECK_POSIX( pthread_mutex_lock( &q->mtx ) ); - ASSERT( ++loops < 20 ); /* detect infinite loops */ + ASSERT( ++loops < 200 ); /* detect infinite loops */ } - + /* sanity check */ ASSERT(FD_IS_LIST_EMPTY(&q->list)); - + /* And destroy it */ CHECK_POSIX( pthread_mutex_unlock( &q->mtx ) ); - + CHECK_POSIX_DO( pthread_cond_destroy( &q->cond_pull ), ); - + CHECK_POSIX_DO( pthread_cond_destroy( &q->cond_push ), ); - + CHECK_POSIX_DO( pthread_mutex_destroy( &q->mtx ), ); - + free(q); *queue = NULL; - + return 0; } /* Move the content of old into new, and update loc_update atomically. We leave the old queue empty but valid */ int fd_fifo_move ( struct fifo * old, struct fifo * new, struct fifo ** loc_update ) { +#ifndef NDEBUG int loops = 0; - +#endif + TRACE_ENTRY("%p %p %p", old, new, loc_update); CHECK_PARAMS( CHECK_FIFO( old ) && CHECK_FIFO( new )); - + CHECK_PARAMS( ! old->data ); if (new->high) { TODO("Implement support for thresholds in fd_fifo_move..."); } - + /* Update loc_update */ if (loc_update) *loc_update = new; - + /* Lock the queues */ CHECK_POSIX( pthread_mutex_lock( &old->mtx ) ); - + CHECK_PARAMS_DO( (! old->thrs_push), { pthread_mutex_unlock( &old->mtx ); return EINVAL; } ); - + CHECK_POSIX( pthread_mutex_lock( &new->mtx ) ); - + /* Any waiting thread on the old queue returns an error */ old->eyec = 0xdead; while (old->thrs) { CHECK_POSIX( pthread_mutex_unlock( &old->mtx )); CHECK_POSIX( pthread_cond_signal( &old->cond_pull ) ); usleep(1000); - + CHECK_POSIX( pthread_mutex_lock( &old->mtx ) ); ASSERT( loops < 20 ); /* detect infinite loops */ } - + /* Move all data from old to new */ fd_list_move_end( &new->list, &old->list ); if (old->count && (!new->count)) { CHECK_POSIX( pthread_cond_signal(&new->cond_pull) ); } new->count += old->count; - + /* Reset old */ old->count = 0; old->eyec = FIFO_EYEC; - + /* Merge the stats in the new queue */ new->total_items += old->total_items; old->total_items = 0; - + new->total_time.tv_nsec += old->total_time.tv_nsec; new->total_time.tv_sec += old->total_time.tv_sec + (new->total_time.tv_nsec / 1000000000); new->total_time.tv_nsec %= 1000000000; old->total_time.tv_nsec = 0; old->total_time.tv_sec = 0; - + new->blocking_time.tv_nsec += old->blocking_time.tv_nsec; new->blocking_time.tv_sec += old->blocking_time.tv_sec + (new->blocking_time.tv_nsec / 1000000000); new->blocking_time.tv_nsec %= 1000000000; old->blocking_time.tv_nsec = 0; old->blocking_time.tv_sec = 0; - + /* Unlock, we're done */ CHECK_POSIX( pthread_mutex_unlock( &new->mtx ) ); CHECK_POSIX( pthread_mutex_unlock( &old->mtx ) ); - + return 0; } /* Get the information on the queue */ -int fd_fifo_getstats( struct fifo * queue, int * current_count, int * limit_count, int * highest_count, long long * total_count, +int fd_fifo_getstats( struct fifo * queue, int * current_count, int * limit_count, int * highest_count, long long * total_count, struct timespec * total, struct timespec * blocking, struct timespec * last) { TRACE_ENTRY( "%p %p %p %p %p %p %p %p", queue, current_count, limit_count, highest_count, total_count, total, blocking, last); - + + if (queue == NULL) { + /* It is not an error if the queue is not available; happens e.g. when peers disappear */ + return 0; + } + /* Check the parameters */ CHECK_PARAMS( CHECK_FIFO( queue ) ); - + /* lock the queue */ CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) ); - + if (current_count) *current_count = queue->count; - + if (limit_count) *limit_count = queue->max; - + if (highest_count) *highest_count = queue->highest_ever; - + if (total_count) *total_count = queue->total_items; - + if (total) memcpy(total, &queue->total_time, sizeof(struct timespec)); - + if (blocking) memcpy(blocking, &queue->blocking_time, sizeof(struct timespec)); - + if (last) memcpy(last, &queue->last_time, sizeof(struct timespec)); - + /* Unlock */ CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) ); - + /* Done */ return 0; } @@ -328,7 +349,7 @@ int fd_fifo_length ( struct fifo * queue ) { if ( !CHECK_FIFO( queue ) ) return 0; - + return queue->count; /* Let's hope it's read atomically, since we are not locking... */ } @@ -336,23 +357,23 @@ int fd_fifo_length ( struct fifo * queue ) int fd_fifo_setthrhd ( struct fifo * queue, void * data, uint16_t high, void (*h_cb)(struct fifo *, void **), uint16_t low, void (*l_cb)(struct fifo *, void **) ) { TRACE_ENTRY( "%p %p %hu %p %hu %p", queue, data, high, h_cb, low, l_cb ); - + /* Check the parameters */ CHECK_PARAMS( CHECK_FIFO( queue ) && (high > low) && (queue->data == NULL) ); - + /* lock the queue */ CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) ); - + /* Save the values */ queue->high = high; queue->low = low; queue->data = data; queue->h_cb = h_cb; queue->l_cb = l_cb; - + /* Unlock */ CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) ); - + /* Done */ return 0; } @@ -363,13 +384,13 @@ static void fifo_cleanup_push(void * queue) { struct fifo * q = (struct fifo *)queue; TRACE_ENTRY( "%p", queue ); - + /* The thread has been cancelled, therefore it does not wait on the queue anymore */ q->thrs_push--; - + /* Now unlock the queue, and we're done */ CHECK_POSIX_DO( pthread_mutex_unlock( &q->mtx ), /* nothing */ ); - + /* End of cleanup handler */ return; } @@ -381,37 +402,40 @@ int fd_fifo_post_internal ( struct fifo * queue, void ** item, int skip_max ) struct fifo_item * new; int call_cb = 0; struct timespec posted_on, queued_on; - + /* Get the timing of this call */ CHECK_SYS( clock_gettime(CLOCK_REALTIME, &posted_on) ); - + /* lock the queue */ CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) ); - + if ((!skip_max) && (queue->max)) { while (queue->count >= queue->max) { int ret = 0; - + /* We have to wait for an item to be pulled */ queue->thrs_push++ ; pthread_cleanup_push( fifo_cleanup_push, queue); ret = pthread_cond_wait( &queue->cond_push, &queue->mtx ); pthread_cleanup_pop(0); queue->thrs_push-- ; - + +#ifdef NDEBUG + (void)ret; +#endif ASSERT( ret == 0 ); } } - + /* Create a new list item */ CHECK_MALLOC_DO( new = malloc (sizeof (struct fifo_item)) , { pthread_mutex_unlock( &queue->mtx ); return ENOMEM; } ); - + fd_list_init(&new->item, *item); *item = NULL; - + /* Add the new item at the end */ fd_list_insert_before( &queue->list, &new->item); queue->count++; @@ -421,10 +445,10 @@ int fd_fifo_post_internal ( struct fifo * queue, void ** item, int skip_max ) call_cb = 1; queue->highest = queue->count; } - + /* store timing */ memcpy(&new->posted_on, &posted_on, sizeof(struct timespec)); - + /* update queue timing info "blocking time" */ { long long blocked_ns; @@ -435,7 +459,7 @@ int fd_fifo_post_internal ( struct fifo * queue, void ** item, int skip_max ) queue->blocking_time.tv_sec += blocked_ns / 1000000000; queue->blocking_time.tv_nsec = blocked_ns % 1000000000; } - + /* Signal if threads are asleep */ if (queue->thrs > 0) { CHECK_POSIX( pthread_cond_signal(&queue->cond_pull) ); @@ -444,14 +468,14 @@ int fd_fifo_post_internal ( struct fifo * queue, void ** item, int skip_max ) /* cascade */ CHECK_POSIX( pthread_cond_signal(&queue->cond_push) ); } - + /* Unlock */ CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) ); - + /* Call high-watermark cb as needed */ if (call_cb && queue->h_cb) (*queue->h_cb)(queue, &queue->data); - + /* Done */ return 0; } @@ -460,24 +484,24 @@ int fd_fifo_post_internal ( struct fifo * queue, void ** item, int skip_max ) int fd_fifo_post_int ( struct fifo * queue, void ** item ) { TRACE_ENTRY( "%p %p", queue, item ); - + /* Check the parameters */ CHECK_PARAMS( CHECK_FIFO( queue ) && item && *item ); - + return fd_fifo_post_internal ( queue,item, 0 ); - + } /* Post a new item in the queue, not blocking */ int fd_fifo_post_noblock ( struct fifo * queue, void ** item ) { TRACE_ENTRY( "%p %p", queue, item ); - + /* Check the parameters */ CHECK_PARAMS( CHECK_FIFO( queue ) && item && *item ); - + return fd_fifo_post_internal ( queue,item, 1 ); - + } /* Pop the first item from the queue */ @@ -486,35 +510,35 @@ static void * mq_pop(struct fifo * queue) void * ret = NULL; struct fifo_item * fi; struct timespec now; - + ASSERT( ! FD_IS_LIST_EMPTY(&queue->list) ); - + fi = (struct fifo_item *)(queue->list.next); ret = fi->item.o; fd_list_unlink(&fi->item); queue->count--; queue->total_items++; - + /* Update the timings */ CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), goto skip_timing ); { long long elapsed = (now.tv_sec - fi->posted_on.tv_sec) * 1000000000; elapsed += now.tv_nsec - fi->posted_on.tv_nsec; - + queue->last_time.tv_sec = elapsed / 1000000000; queue->last_time.tv_nsec = elapsed % 1000000000; - + elapsed += queue->total_time.tv_nsec; queue->total_time.tv_sec += elapsed / 1000000000; queue->total_time.tv_nsec = elapsed % 1000000000; } -skip_timing: +skip_timing: free(fi); - + if (queue->thrs_push) { CHECK_POSIX_DO( pthread_cond_signal( &queue->cond_push ), ); } - + return ret; } @@ -523,12 +547,12 @@ static __inline__ int test_l_cb(struct fifo * queue) { if ((queue->high == 0) || (queue->low == 0) || (queue->l_cb == 0)) return 0; - + if (((queue->count % queue->high) == queue->low) && (queue->highest > queue->count)) { queue->highest -= queue->high; return 1; } - + return 0; } @@ -537,15 +561,15 @@ int fd_fifo_tryget_int ( struct fifo * queue, void ** item ) { int wouldblock = 0; int call_cb = 0; - + TRACE_ENTRY( "%p %p", queue, item ); - + /* Check the parameters */ CHECK_PARAMS( CHECK_FIFO( queue ) && item ); - + /* lock the queue */ CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) ); - + /* Check queue status */ if (queue->count > 0) { got_item: @@ -562,18 +586,18 @@ got_item: if (queue->count > 0) goto got_item; } - + wouldblock = 1; *item = NULL; } - + /* Unlock */ CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) ); - + /* Call low watermark callback as needed */ if (call_cb) (*queue->l_cb)(queue, &queue->data); - + /* Done */ return wouldblock ? EWOULDBLOCK : 0; } @@ -583,13 +607,13 @@ static void fifo_cleanup(void * queue) { struct fifo * q = (struct fifo *)queue; TRACE_ENTRY( "%p", queue ); - + /* The thread has been cancelled, therefore it does not wait on the queue anymore */ q->thrs--; - + /* Now unlock the queue, and we're done */ CHECK_POSIX_DO( pthread_mutex_unlock( &q->mtx ), /* nothing */ ); - + /* End of cleanup handler */ return; } @@ -599,16 +623,16 @@ static int fifo_tget ( struct fifo * queue, void ** item, int istimed, const str { int call_cb = 0; int ret = 0; - + /* Check the parameters */ CHECK_PARAMS( CHECK_FIFO( queue ) && item && (abstime || !istimed) ); - + /* Initialize the return value */ *item = NULL; - + /* lock the queue */ CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) ); - + awaken: /* Check queue status */ if (!CHECK_FIFO( queue )) { @@ -617,7 +641,7 @@ awaken: TRACE_DEBUG(FULL, "The queue is being destroyed -> EPIPE"); return EPIPE; } - + if (queue->count > 0) { /* There are items in the queue, so pick the first one */ *item = mq_pop(queue); @@ -635,17 +659,17 @@ awaken: queue->thrs-- ; if (ret == 0) goto awaken; /* test for spurious wake-ups */ - + /* otherwise (ETIMEDOUT / other error) just continue */ } - + /* Unlock */ CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) ); - + /* Call low watermark callback as needed */ if (call_cb) (*queue->l_cb)(queue, &queue->data); - + /* Done */ return ret; } @@ -669,13 +693,13 @@ int fd_fifo_select ( struct fifo * queue, const struct timespec *abstime ) { int ret = 0; TRACE_ENTRY( "%p %p", queue, abstime ); - + CHECK_PARAMS_DO( CHECK_FIFO( queue ), return -EINVAL ); - + /* lock the queue */ CHECK_POSIX_DO( pthread_mutex_lock( &queue->mtx ), return -__ret__ ); - -awaken: + +awaken: ret = (queue->count > 0 ) ? queue->count : 0; if ((ret == 0) && (abstime != NULL)) { /* We have to wait for a new item */ @@ -686,15 +710,15 @@ awaken: queue->thrs-- ; if (ret == 0) goto awaken; /* test for spurious wake-ups */ - + if (ret == ETIMEDOUT) ret = 0; - else + else ret = -ret; } - + /* Unlock */ CHECK_POSIX_DO( pthread_mutex_unlock( &queue->mtx ), return -__ret__ ); - + return ret; } diff --git a/libfdproto/log.c b/libfdproto/log.c index 7bbe307..c0b6ec5 100644 --- a/libfdproto/log.c +++ b/libfdproto/log.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2015, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -112,6 +112,7 @@ static void fd_internal_logger( int printlevel, const char *format, va_list ap ) switch(printlevel) { case FD_LOG_ANNOYING: printf("%s A ", (use_colors == 1) ? "\e[0;37m" : ""); break; case FD_LOG_DEBUG: printf("%s DBG ", (use_colors == 1) ? "\e[0;37m" : ""); break; + case FD_LOG_INFO: printf("%sINFO ", (use_colors == 1) ? "\e[1;37m" : ""); break; case FD_LOG_NOTICE: printf("%sNOTI ", (use_colors == 1) ? "\e[1;37m" : ""); break; case FD_LOG_ERROR: printf("%sERROR ", (use_colors == 1) ? "\e[0;31m" : ""); break; case FD_LOG_FATAL: printf("%sFATAL! ", (use_colors == 1) ? "\e[0;31m" : ""); break; diff --git a/libfdproto/messages.c b/libfdproto/messages.c index ed23f0e..ed8186f 100644 --- a/libfdproto/messages.c +++ b/libfdproto/messages.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2015, WIDE Project and NICT * +* Copyright (c) 2020, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -596,29 +596,29 @@ int fd_msg_avp_add ( msg_or_avp * reference, enum msg_brw_dir dir, struct avp *a return 0; } -/* Search a given AVP model in a message */ -int fd_msg_search_avp ( struct msg * msg, struct dict_object * what, struct avp ** avp ) +/* Search a given AVP model in a message or AVP */ +int fd_msg_search_avp ( msg_or_avp * reference, struct dict_object * what, struct avp ** avp ) { struct avp * nextavp; struct dict_avp_data dictdata; enum dict_object_type dicttype; - TRACE_ENTRY("%p %p %p", msg, what, avp); + TRACE_ENTRY("%p %p %p", reference, what, avp); - CHECK_PARAMS( CHECK_MSG(msg) && what ); + CHECK_PARAMS( VALIDATE_OBJ(reference) && what ); CHECK_PARAMS( (fd_dict_gettype(what, &dicttype) == 0) && (dicttype == DICT_AVP) ); CHECK_FCT( fd_dict_getval(what, &dictdata) ); - /* Loop on all top AVPs */ - CHECK_FCT( fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, (void *)&nextavp, NULL) ); + /* Loop on all top AVPs in message or AVP */ + CHECK_FCT( fd_msg_browse(reference, MSG_BRW_FIRST_CHILD, (void *)&nextavp, NULL) ); while (nextavp) { if ( (nextavp->avp_public.avp_code == dictdata.avp_code) && (nextavp->avp_public.avp_vendor == dictdata.avp_vendor) ) /* always 0 if no V flag */ break; - /* Otherwise move to next AVP in the message */ + /* Otherwise move to next AVP in the message or AVP */ CHECK_FCT( fd_msg_browse(nextavp, MSG_BRW_NEXT, (void *)&nextavp, NULL) ); } @@ -1260,6 +1260,7 @@ int fd_msg_anscb_associate( struct msg * msg, void ( *anscb)(void *, struct msg } if (expirecb) { msg->msg_cb.expirecb = expirecb; + msg->msg_cb.data = data; if (timeout) { memcpy(&msg->msg_cb.timeout, timeout, sizeof(struct timespec)); } @@ -1945,6 +1946,14 @@ static int parsebuf_list(unsigned char * buf, size_t buflen, struct fd_list * he offset += 4; } + /* Check the length is valid */ + if ( avp->avp_public.avp_len < GETAVPHDRSZ(avp->avp_public.avp_flags) ) { + TRACE_DEBUG(INFO, "Invalid AVP size %d", + avp->avp_public.avp_len); + free(avp); + return EBADMSG; + } + /* Check there is enough remaining data in the buffer */ if ( (avp->avp_public.avp_len > GETAVPHDRSZ(avp->avp_public.avp_flags)) && (buflen - offset < avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags))) { @@ -1991,6 +2000,10 @@ int fd_msg_parse_buffer ( unsigned char ** buffer, size_t buflen, struct msg ** TRACE_DEBUG(INFO, "Truncated message (%zd / %d)", buflen, msglen ); return EBADMSG; } + if ( msglen < GETMSGHDRSZ() ) { + TRACE_DEBUG(INFO, "Invalid message length (%d)", msglen ); + return EBADMSG; + } /* Create a new object */ CHECK_MALLOC( new = malloc (sizeof(struct msg)) ); @@ -2233,7 +2246,7 @@ static int parsedict_do_avp(struct dictionary * dict, struct avp * avp, int mand if (error_info) { error_info->pei_errcode = "DIAMETER_INVALID_AVP_VALUE"; error_info->pei_avp = avp; - strncpy(error_message, err, sizeof(error_message)); + snprintf(error_message, sizeof(error_message), "%s", err); error_info->pei_message = error_message; } else { char * buf = NULL; @@ -2283,7 +2296,9 @@ static int parsedict_do_msg(struct dictionary * dict, struct msg * msg, int only /* First, check if we already have a model. */ if (msg->msg_model != NULL) { /* Check if this model is still valid for the message data */ +#ifndef NDEBUG enum dict_object_type dicttype; +#endif struct dict_cmd_data data; ASSERT(((fd_dict_gettype(msg->msg_model, &dicttype) == 0) && (dicttype == DICT_COMMAND))); (void)fd_dict_getval( msg->msg_model, &data); diff --git a/libfdproto/sessions.c b/libfdproto/sessions.c index b6c94fa..ca81dba 100644 --- a/libfdproto/sessions.c +++ b/libfdproto/sessions.c @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2019, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -34,7 +34,7 @@ *********************************************************************************************************/ /* Sessions module. - * + * * Basic functionalities to help implementing User sessions state machines from RFC3588. */ @@ -91,15 +91,15 @@ struct state { /* Session object, one for each value of Session-Id AVP */ struct session { int eyec; /* Eyecatcher, SI_EYEC */ - + os0_t sid; /* The \0-terminated Session-Id */ size_t sidlen; /* cached length of sid */ uint32_t hash; /* computed hash of sid */ struct fd_list chain_h;/* chaining in the hash table of sessions. */ - + struct timespec timeout;/* Timeout date for the session */ struct fd_list expire; /* List of expiring sessions, ordered by timeouts. */ - + pthread_mutex_t stlock; /* A lock to protect the list of states associated with this session */ struct fd_list states; /* Sentinel for the list of states of this session. */ int msg_cnt;/* Reference counter for the messages pointing to this session */ @@ -125,7 +125,7 @@ static pthread_mutex_t sid_lock = PTHREAD_MUTEX_INITIALIZER; /* Expiring sessions management */ static struct fd_list exp_sentinel = FD_LIST_INITIALIZER(exp_sentinel); /* list of sessions ordered by their timeout date */ static pthread_mutex_t exp_lock = PTHREAD_MUTEX_INITIALIZER; /* lock protecting the list. */ -static pthread_cond_t exp_cond = PTHREAD_COND_INITIALIZER; /* condvar used by the expiry mecahinsm. */ +static pthread_cond_t exp_cond = PTHREAD_COND_INITIALIZER; /* condvar used by the expiry mechainsm. */ static pthread_t exp_thr = (pthread_t)NULL; /* The expiry thread that handles cleanup of expired sessions */ /* Hierarchy of the locks, to avoid deadlocks: @@ -140,27 +140,27 @@ static pthread_t exp_thr = (pthread_t)NULL; /* The expiry thread that handles c static struct session * new_session(os0_t sid, size_t sidlen, uint32_t hash) { struct session * sess; - + TRACE_ENTRY("%p %zd", sid, sidlen); CHECK_PARAMS_DO( sid && sidlen, return NULL ); - + CHECK_MALLOC_DO( sess = malloc(sizeof(struct session)), return NULL ); memset(sess, 0, sizeof(struct session)); - + sess->eyec = SI_EYEC; - + sess->sid = sid; sess->sidlen = sidlen; sess->hash = hash; fd_list_init(&sess->chain_h, sess); - + CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &sess->timeout), return NULL ); sess->timeout.tv_sec += SESS_DEFAULT_LIFETIME; fd_list_init(&sess->expire, sess); - + CHECK_POSIX_DO( pthread_mutex_init(&sess->stlock, NULL), return NULL ); fd_list_init(&sess->states, sess); - + return sess; } @@ -174,21 +174,21 @@ static void del_session(struct session * s) CHECK_POSIX_DO( pthread_mutex_destroy(&s->stlock), /* continue */ ); free(s); } - + /* The expiry thread */ static void * exp_fct(void * arg) { fd_log_threadname ( "Session/expire" ); TRACE_ENTRY( "" ); - - + + do { struct timespec now; struct session * first; - + CHECK_POSIX_DO( pthread_mutex_lock(&exp_lock), break ); pthread_cleanup_push( fd_cleanup_mutex, &exp_lock ); -again: +again: /* Check if there are expiring sessions available */ if (FD_IS_LIST_EMPTY(&exp_sentinel)) { /* Just wait for a change or cancelation */ @@ -196,39 +196,53 @@ again: /* Restart the loop on wakeup */ goto again; } - + /* Get the pointer to the session that expires first */ first = (struct session *)(exp_sentinel.next->o); ASSERT( VALIDATE_SI(first) ); - + /* Get the current time */ CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), break ); /* If first session is not expired, we just wait until it happens */ if ( TS_IS_INFERIOR( &now, &first->timeout ) ) { - - CHECK_POSIX_DO2( pthread_cond_timedwait( &exp_cond, &exp_lock, &first->timeout ), - ETIMEDOUT, /* ETIMEDOUT is a normal error, continue */, - /* on other error, */ break ); - - /* on wakeup, loop */ - goto again; + int ret; + + ret = pthread_cond_timedwait(&exp_cond, &exp_lock, &first->timeout); + switch (ret) { + case 0: + case ETIMEDOUT: + /* on wakeup or time-out, loop */ + goto again; + case EINVAL: + if (clock_gettime(CLOCK_REALTIME, &now) < 0) { + break; + } + if (TS_IS_INFERIOR(&now, &first->timeout)) { + TRACE_DEBUG(FULL, "'pthread_cond_timedwait(&exp_cond, &exp_lock, &first->timeout)' : timer expired before loop could start"); + goto again; + } + /* FALLTHROUGH */ + default: + TRACE_ERROR("ERROR: in 'pthread_cond_timedwait(&exp_cond, &exp_lock, &first->timeout)' :\t%s", strerror(ret)); + break; + } } - + /* Now, the first session in the list is expired; destroy it */ pthread_cleanup_pop( 0 ); CHECK_POSIX_DO( pthread_mutex_unlock(&exp_lock), break ); - + CHECK_FCT_DO( fd_sess_destroy( &first ), break ); - + } while (1); - + TRACE_DEBUG(INFO, "A system error occurred in session module! Expiry thread is terminating..."); ASSERT(0); return NULL; } - - + + /********************************************************************************************************/ @@ -236,19 +250,19 @@ again: int fd_sess_init(void) { int i; - + TRACE_ENTRY( "" ); - + /* Initialize the global counters */ sid_h = (uint32_t) time(NULL); sid_l = 0; - + /* Initialize the hash table */ for (i = 0; i < sizeof(sess_hash) / sizeof(sess_hash[0]); i++) { fd_list_init( &sess_hash[i].sentinel, NULL ); CHECK_POSIX( pthread_mutex_init(&sess_hash[i].lock, NULL) ); } - + return 0; } @@ -257,7 +271,7 @@ int fd_sess_start(void) { /* Start session garbage collector (expiry) */ CHECK_POSIX( pthread_create(&exp_thr, NULL, exp_fct, NULL) ); - + return 0; } @@ -266,9 +280,9 @@ void fd_sess_fini(void) { TRACE_ENTRY(""); CHECK_FCT_DO( fd_thr_term(&exp_thr), /* continue */ ); - + /* Destroy all sessions in the hash table, and the hash table itself? -- How to do it without a race condition ? */ - + return; } @@ -276,28 +290,28 @@ void fd_sess_fini(void) int fd_sess_handler_create ( struct session_handler ** handler, void (*cleanup)(struct sess_state *, os0_t, void *), session_state_dump dumper, void * opaque ) { struct session_handler *new; - + TRACE_ENTRY("%p %p", handler, cleanup); - + CHECK_PARAMS( handler && cleanup ); - + CHECK_MALLOC( new = malloc(sizeof(struct session_handler)) ); memset(new, 0, sizeof(struct session_handler)); - + CHECK_POSIX( pthread_mutex_lock(&hdl_lock) ); new->id = ++hdl_id; CHECK_POSIX( pthread_mutex_unlock(&hdl_lock) ); - + new->eyec = SH_EYEC; new->cleanup = cleanup; new->state_dump = dumper; new->opaque = opaque; - + *handler = new; return 0; } -/* Destroy a handler, and all states attached to this handler. This operation is very slow but we don't care since it's rarely used. +/* Destroy a handler, and all states attached to this handler. This operation is very slow but we don't care since it's rarely used. * Note that it's better to call this function after all sessions have been deleted... */ int fd_sess_handler_destroy ( struct session_handler ** handler, void ** opaque ) { @@ -305,20 +319,20 @@ int fd_sess_handler_destroy ( struct session_handler ** handler, void ** opaque /* place to save the list of states to be cleaned up. We do it after finding them to avoid deadlocks. the "o" field becomes a copy of the sid. */ struct fd_list deleted_states = FD_LIST_INITIALIZER( deleted_states ); int i; - + TRACE_ENTRY("%p", handler); CHECK_PARAMS( handler && VALIDATE_SH(*handler) ); - + del = *handler; *handler = NULL; - + del->eyec = 0xdead; /* The handler is not valid anymore for any other operation */ - + /* Now find all sessions with data registered for this handler, and move this data to the deleted_states list. */ for (i = 0; i < sizeof(sess_hash) / sizeof(sess_hash[0]); i++) { struct fd_list * li_si; CHECK_POSIX( pthread_mutex_lock(&sess_hash[i].lock) ); - + for (li_si = sess_hash[i].sentinel.next; li_si != &sess_hash[i].sentinel; li_si = li_si->next) { /* for each session in the hash line */ struct fd_list * li_st; struct session * sess = (struct session *)(li_si->o); @@ -340,7 +354,7 @@ int fd_sess_handler_destroy ( struct session_handler ** handler, void ** opaque } CHECK_POSIX( pthread_mutex_unlock(&sess_hash[i].lock) ); } - + /* Now, delete all states after calling their cleanup handler */ while (!FD_IS_LIST_EMPTY(&deleted_states)) { struct state * st = (struct state *)(deleted_states.next->o); @@ -349,13 +363,13 @@ int fd_sess_handler_destroy ( struct session_handler ** handler, void ** opaque fd_list_unlink(&st->chain); free(st); } - + if (opaque) *opaque = del->opaque; - + /* Free the handler */ free(del); - + return 0; } @@ -371,20 +385,20 @@ int fd_sess_new ( struct session ** session, DiamId_t diamid, size_t diamidlen, struct fd_list * li; int found = 0; int ret = 0; - + TRACE_ENTRY("%p %p %zd %p %zd", session, diamid, diamidlen, opt, optlen); CHECK_PARAMS( session && (diamid || opt) ); - if (diamid) { + if (diamid) { if (!diamidlen) { diamidlen = strlen(diamid); - } + } /* We check if the string is a valid DiameterIdentity */ CHECK_PARAMS( fd_os_is_valid_DiameterIdentity((uint8_t *)diamid, diamidlen) ); } else { diamidlen = 0; } - if (opt) { + if (opt) { if (!optlen) { optlen = strlen((char *)opt); } else { @@ -393,7 +407,7 @@ int fd_sess_new ( struct session ** session, DiamId_t diamid, size_t diamidlen, } else { optlen = 0; } - + /* Ok, first create the identifier for the string */ if (diamid == NULL) { /* opt is the full string */ @@ -409,49 +423,49 @@ int fd_sess_new ( struct session ** session, DiamId_t diamid, size_t diamidlen, sidlen += 1 + optlen; /* ';opt' */ sidlen++; /* space for the final \0 also */ CHECK_MALLOC( sid = malloc(sidlen) ); - + CHECK_POSIX( pthread_mutex_lock(&sid_lock) ); if ( ++sid_l == 0 ) /* overflow */ ++sid_h; sid_h_cpy = sid_h; sid_l_cpy = sid_l; CHECK_POSIX( pthread_mutex_unlock(&sid_lock) ); - + if (opt) { sidlen = snprintf((char*)sid, sidlen, "%.*s;%u;%u;%.*s", (int)diamidlen, diamid, sid_h_cpy, sid_l_cpy, (int)optlen, opt); } else { sidlen = snprintf((char*)sid, sidlen, "%.*s;%u;%u", (int)diamidlen, diamid, sid_h_cpy, sid_l_cpy); } } - + hash = fd_os_hash(sid, sidlen); - + /* Now find the place to add this object in the hash table. */ CHECK_POSIX( pthread_mutex_lock( H_LOCK(hash) ) ); pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(hash) ); - + for (li = H_LIST(hash)->next; li != H_LIST(hash); li = li->next) { int cmp; struct session * s = (struct session *)(li->o); - + /* The list is ordered by hash and sid (in case of collisions) */ if (s->hash < hash) continue; if (s->hash > hash) break; - + cmp = fd_os_cmp(s->sid, s->sidlen, sid, sidlen); if (cmp < 0) continue; if (cmp > 0) break; - + /* A session with the same sid was already in the hash table */ found = 1; *session = s; break; } - + /* If the session did not exist, we can create it & link it in global tables */ if (!found) { CHECK_MALLOC_DO(sess = new_session(sid, sidlen, hash), @@ -460,16 +474,16 @@ int fd_sess_new ( struct session ** session, DiamId_t diamid, size_t diamidlen, free(sid); goto out; } ); - + fd_list_insert_before(li, &sess->chain_h); /* hash table */ sess->msg_cnt++; } else { free(sid); - - CHECK_POSIX( pthread_mutex_lock(&(*session)->stlock) ); + + CHECK_POSIX( pthread_mutex_lock(&(*session)->stlock) ); (*session)->msg_cnt++; - CHECK_POSIX( pthread_mutex_unlock(&(*session)->stlock) ); - + CHECK_POSIX( pthread_mutex_unlock(&(*session)->stlock) ); + /* it was found: was it previously destroyed? */ if ((*session)->is_destroyed == 0) { ret = EALREADY; @@ -478,13 +492,13 @@ int fd_sess_new ( struct session ** session, DiamId_t diamid, size_t diamidlen, /* the session was marked destroyed, let's re-activate it. */ sess = *session; sess->is_destroyed = 0; - + /* update the expiry time */ CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &sess->timeout), { ASSERT(0); } ); sess->timeout.tv_sec += SESS_DEFAULT_LIFETIME; } } - + /* We must insert in the expiry list */ CHECK_POSIX( pthread_mutex_lock( &exp_lock ) ); pthread_cleanup_push( fd_cleanup_mutex, &exp_lock ); @@ -511,10 +525,10 @@ out: ; pthread_cleanup_pop(0); CHECK_POSIX( pthread_mutex_unlock( H_LOCK(hash) ) ); - + if (ret) /* in case of error */ return ret; - + *session = sess; return 0; } @@ -523,28 +537,28 @@ out: int fd_sess_fromsid_msg ( uint8_t * sid, size_t len, struct session ** session, int * new) { int ret; - + TRACE_ENTRY("%p %zd %p %p", sid, len, session, new); CHECK_PARAMS( sid && session ); - + if (!fd_os_is_valid_os0(sid,len)) { TRACE_DEBUG(INFO, "Warning: a Session-Id value contains \\0 chars... (len:%zd, begin:'%.*s') => Debug messages may be truncated.", len, (int)len, sid); } - + /* All the work is done in sess_new */ ret = fd_sess_new ( session, NULL, 0, sid, len ); switch (ret) { case 0: case EALREADY: break; - + default: CHECK_FCT(ret); } - + if (new) *new = ret ? 0 : 1; - + return 0; } @@ -552,13 +566,13 @@ int fd_sess_fromsid_msg ( uint8_t * sid, size_t len, struct session ** session, int fd_sess_getsid ( struct session * session, os0_t * sid, size_t * sidlen ) { TRACE_ENTRY("%p %p", session, sid); - + CHECK_PARAMS( VALIDATE_SI(session) && sid ); - + *sid = session->sid; if (sidlen) *sidlen = session->sidlen; - + return 0; } @@ -566,18 +580,18 @@ int fd_sess_getsid ( struct session * session, os0_t * sid, size_t * sidlen ) int fd_sess_settimeout( struct session * session, const struct timespec * timeout ) { struct fd_list * li; - + TRACE_ENTRY("%p %p", session, timeout); CHECK_PARAMS( VALIDATE_SI(session) && timeout ); - + /* Lock -- do we need to lock the hash table as well? I don't think so... */ CHECK_POSIX( pthread_mutex_lock( &exp_lock ) ); pthread_cleanup_push( fd_cleanup_mutex, &exp_lock ); - + /* Update the timeout */ fd_list_unlink(&session->expire); memcpy(&session->timeout, timeout, sizeof(struct timespec)); - + /* Find the new position in expire list. We take it in normal order */ for (li = exp_sentinel.next; li != &exp_sentinel; li = li->next) { struct session * s = (struct session *)(li->o); @@ -597,7 +611,7 @@ int fd_sess_settimeout( struct session * session, const struct timespec * timeou /* We're done */ pthread_cleanup_pop(0); CHECK_POSIX( pthread_mutex_unlock( &exp_lock ) ); - + return 0; } @@ -608,28 +622,30 @@ int fd_sess_destroy ( struct session ** session ) int destroy_now; os0_t sid; int ret = 0; - + /* place to save the list of states to be cleaned up. We do it after finding them to avoid deadlocks. the "o" field becomes a copy of the sid. */ struct fd_list deleted_states = FD_LIST_INITIALIZER( deleted_states ); - + TRACE_ENTRY("%p", session); CHECK_PARAMS( session && VALIDATE_SI(*session) ); - + sess = *session; *session = NULL; - + /* Lock the hash line */ CHECK_POSIX( pthread_mutex_lock( H_LOCK(sess->hash) ) ); pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(sess->hash) ); - + /* Unlink from the expiry list */ CHECK_POSIX_DO( pthread_mutex_lock( &exp_lock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } ); + pthread_cleanup_push( fd_cleanup_mutex, &exp_lock ); if (!FD_IS_LIST_EMPTY(&sess->expire)) { sess_cnt--; fd_list_unlink( &sess->expire ); /* no need to signal the condition here */ } + pthread_cleanup_pop(0); CHECK_POSIX_DO( pthread_mutex_unlock( &exp_lock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } ); - + /* Now move all states associated to this session into deleted_states */ CHECK_POSIX_DO( pthread_mutex_lock( &sess->stlock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } ); while (!FD_IS_LIST_EMPTY(&sess->states)) { @@ -638,7 +654,7 @@ int fd_sess_destroy ( struct session ** session ) fd_list_insert_before(&deleted_states, &st->chain); } CHECK_POSIX_DO( pthread_mutex_unlock( &sess->stlock ), { ASSERT(0); /* otherwise cleanup handler is not pop'd */ } ); - + /* Mark the session as destroyed */ destroy_now = (sess->msg_cnt == 0); if (destroy_now) { @@ -650,10 +666,10 @@ int fd_sess_destroy ( struct session ** session ) } pthread_cleanup_pop(0); CHECK_POSIX( pthread_mutex_unlock( H_LOCK(sess->hash) ) ); - + if (ret) return ret; - + /* Now, really delete the states */ while (!FD_IS_LIST_EMPTY(&deleted_states)) { struct state * st = (struct state *)(deleted_states.next->o); @@ -662,14 +678,14 @@ int fd_sess_destroy ( struct session ** session ) (*st->hdl->cleanup)(st->state, sid, st->hdl->opaque); free(st); } - + /* Finally, destroy the session itself, if it is not referrenced by any message anymore */ if (destroy_now) { del_session(sess); } else { free(sid); } - + return 0; } @@ -679,20 +695,21 @@ int fd_sess_reclaim ( struct session ** session ) struct session * sess; uint32_t hash; int destroy_now = 0; - + TRACE_ENTRY("%p", session); CHECK_PARAMS( session && VALIDATE_SI(*session) ); - + sess = *session; hash = sess->hash; *session = NULL; - + CHECK_POSIX( pthread_mutex_lock( H_LOCK(hash) ) ); pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(hash) ); CHECK_POSIX_DO( pthread_mutex_lock( &sess->stlock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } ); pthread_cleanup_push( fd_cleanup_mutex, &sess->stlock ); CHECK_POSIX_DO( pthread_mutex_lock( &exp_lock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } ); - + pthread_cleanup_push( fd_cleanup_mutex, &exp_lock ); + /* We only do something if the states list is empty */ if (FD_IS_LIST_EMPTY(&sess->states)) { /* In this case, we do as in destroy */ @@ -705,16 +722,17 @@ int fd_sess_reclaim ( struct session ** session ) sess->is_destroyed = 1; } } - + + pthread_cleanup_pop(0); CHECK_POSIX_DO( pthread_mutex_unlock( &exp_lock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } ); pthread_cleanup_pop(0); CHECK_POSIX_DO( pthread_mutex_unlock( &sess->stlock ), { ASSERT(0); /* otherwise, cleanup not poped on FreeBSD */ } ); pthread_cleanup_pop(0); CHECK_POSIX( pthread_mutex_unlock( H_LOCK(hash) ) ); - + if (destroy_now) del_session(sess); - + return 0; } @@ -725,38 +743,38 @@ int fd_sess_state_store ( struct session_handler * handler, struct session * ses struct fd_list * li; int already = 0; int ret = 0; - + TRACE_ENTRY("%p %p %p", handler, session, state); CHECK_PARAMS( handler && VALIDATE_SH(handler) && session && VALIDATE_SI(session) && (!session->is_destroyed) && state ); - + /* Lock the session state list */ CHECK_POSIX( pthread_mutex_lock(&session->stlock) ); pthread_cleanup_push( fd_cleanup_mutex, &session->stlock ); - + /* Create the new state object */ CHECK_MALLOC_DO(new = malloc(sizeof(struct state)), { ret = ENOMEM; goto out; } ); memset(new, 0, sizeof(struct state)); - + new->eyec = SD_EYEC; new->state= *state; fd_list_init(&new->chain, new); new->hdl = handler; - + /* find place for this state in the list */ for (li = session->states.next; li != &session->states; li = li->next) { struct state * st = (struct state *)(li->o); /* The list is ordered by handler's id */ if (st->hdl->id < handler->id) continue; - + if (st->hdl->id == handler->id) { TRACE_DEBUG(INFO, "A state was already stored for session '%s' and handler '%p', at location %p", session->sid, st->hdl, st->state); already = EALREADY; } - + break; } - + if (!already) { fd_list_insert_before(li, &new->chain); *state = NULL; @@ -764,10 +782,10 @@ int fd_sess_state_store ( struct session_handler * handler, struct session * ses free(new); } out: - ; + ; pthread_cleanup_pop(0); CHECK_POSIX( pthread_mutex_unlock(&session->stlock) ); - + return ret ?: already; } @@ -776,35 +794,35 @@ int fd_sess_state_retrieve ( struct session_handler * handler, struct session * { struct fd_list * li; struct state * st = NULL; - + TRACE_ENTRY("%p %p %p", handler, session, state); CHECK_PARAMS( handler && VALIDATE_SH(handler) && session && VALIDATE_SI(session) && state ); - + *state = NULL; - + /* Lock the session state list */ CHECK_POSIX( pthread_mutex_lock(&session->stlock) ); pthread_cleanup_push( fd_cleanup_mutex, &session->stlock ); - + /* find the state in the list */ for (li = session->states.next; li != &session->states; li = li->next) { st = (struct state *)(li->o); - + /* The list is ordered by handler's id */ if (st->hdl->id > handler->id) break; } - + /* If we found the state */ if (st && (st->hdl == handler)) { fd_list_unlink(&st->chain); *state = st->state; free(st); } - + pthread_cleanup_pop(0); CHECK_POSIX( pthread_mutex_unlock(&session->stlock) ); - + return 0; } @@ -813,15 +831,15 @@ int fd_sess_fromsid ( uint8_t * sid, size_t len, struct session ** session, int { TRACE_ENTRY("%p %zd %p %p", sid, len, session, new); CHECK_PARAMS( sid && len && session ); - + /* Get the session object */ CHECK_FCT( fd_sess_fromsid_msg ( sid, len, session, new) ); - + /* Decrease the refcount */ CHECK_POSIX( pthread_mutex_lock(&(*session)->stlock) ); (*session)->msg_cnt--; /* was increased in fd_sess_new */ CHECK_POSIX( pthread_mutex_unlock(&(*session)->stlock) ); - + /* Done */ return 0; } @@ -835,7 +853,7 @@ int fd_sess_ref_msg ( struct session * session ) CHECK_POSIX( pthread_mutex_lock(&session->stlock) ); session->msg_cnt++; CHECK_POSIX( pthread_mutex_unlock(&session->stlock) ); - + return 0; } @@ -843,25 +861,25 @@ int fd_sess_reclaim_msg ( struct session ** session ) { int reclaim; uint32_t hash; - + TRACE_ENTRY("%p", session); CHECK_PARAMS( session && VALIDATE_SI(*session) ); - + /* Lock the hash line to avoid possibility that session is freed while we are reclaiming */ hash = (*session)->hash; CHECK_POSIX( pthread_mutex_lock( H_LOCK(hash)) ); - pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(hash) ); + pthread_cleanup_push( fd_cleanup_mutex, H_LOCK(hash) ); /* Update the msg refcount */ CHECK_POSIX( pthread_mutex_lock(&(*session)->stlock) ); reclaim = (*session)->msg_cnt; (*session)->msg_cnt = reclaim - 1; CHECK_POSIX( pthread_mutex_unlock(&(*session)->stlock) ); - + /* Ok, now unlock the hash line */ pthread_cleanup_pop( 0 ); CHECK_POSIX( pthread_mutex_unlock( H_LOCK(hash) ) ); - + /* and reclaim if no message references the session anymore */ if (reclaim == 1) { CHECK_FCT(fd_sess_reclaim ( session )); @@ -877,9 +895,9 @@ int fd_sess_reclaim_msg ( struct session ** session ) DECLARE_FD_DUMP_PROTOTYPE(fd_sess_dump, struct session * session, int with_states) { FD_DUMP_HANDLE_OFFSET(); - + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{session}(@%p): ", session), return NULL); - + if (!VALIDATE_SI(session)) { CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID/NULL"), return NULL); } else { @@ -889,46 +907,46 @@ DECLARE_FD_DUMP_PROTOTYPE(fd_sess_dump, struct session * session, int with_state strftime(timebuf, sizeof(timebuf), "%D,%T", localtime_r( &session->timeout.tv_sec , &tm )); CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "'%s'(%zd) h:%x m:%d d:%d to:%s.%06ld", session->sid, session->sidlen, session->hash, session->msg_cnt, session->is_destroyed, - timebuf, session->timeout.tv_nsec/1000), + timebuf, session->timeout.tv_nsec/1000), return NULL); - + if (with_states) { struct fd_list * li; CHECK_POSIX_DO( pthread_mutex_lock(&session->stlock), /* ignore */ ); pthread_cleanup_push( fd_cleanup_mutex, &session->stlock ); - + for (li = session->states.next; li != &session->states; li = li->next) { struct state * st = (struct state *)(li->o); CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {state i:%d}(@%p): ", st->hdl->id, st), return NULL); if (st->hdl->state_dump) { - CHECK_MALLOC_DO( (*st->hdl->state_dump)( FD_DUMP_STD_PARAMS, st->state), + CHECK_MALLOC_DO( (*st->hdl->state_dump)( FD_DUMP_STD_PARAMS, st->state), fd_dump_extend( FD_DUMP_STD_PARAMS, "[dumper error]")); } else { CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "<%p>", st->state), return NULL); } } - + pthread_cleanup_pop(0); CHECK_POSIX_DO( pthread_mutex_unlock(&session->stlock), /* ignore */ ); } } - + return *buf; } DECLARE_FD_DUMP_PROTOTYPE(fd_sess_dump_hdl, struct session_handler * handler) { FD_DUMP_HANDLE_OFFSET(); - + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{sesshdl}(@%p): ", handler), return NULL); - + if (!VALIDATE_SH(handler)) { CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "INVALID/NULL"), return NULL); } else { CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "i:%d cl:%p d:%p o:%p", handler->id, handler->cleanup, handler->state_dump, handler->opaque), return NULL); } return *buf; -} +} int fd_sess_getcount(uint32_t *cnt) { |
