2026-02-16 09:02:48 +00:00
# define MODULE_LOG_PREFIX "cache"
# include "globals.h"
# include "module-cacheex.h"
# include "module-cw-cycle-check.h"
# include "oscam-cache.h"
# include "oscam-chk.h"
# include "oscam-client.h"
# include "oscam-ecm.h"
# include "oscam-garbage.h"
# include "oscam-lock.h"
# include "oscam-net.h"
# include "oscam-string.h"
# include "oscam-time.h"
# include "oscam-hashtable.h"
# ifdef CS_CACHEEX_AIO
# include "oscam-log.h"
# endif
// CACHE functions **************************************************************+
struct s_pushclient
{
struct s_client * cl ;
struct s_pushclient * next_push ;
} ;
typedef struct cw_t
{
uint8_t cw [ 16 ] ;
uint8_t odd_even ; // odd/even byte (0x80 0x81)
uint8_t cwc_cycletime ;
uint8_t cwc_next_cw_cycle ;
uint8_t got_bad_cwc ; // used by cycle check
uint16_t caid ; // first caid received
uint32_t prid ; // first prid received
uint16_t srvid ; // first srvid received
struct s_reader * selected_reader ; // first answering: reader
struct s_client * cacheex_src ; // first answering: cacheex client
uint64_t grp ; // updated grp
uint8_t csp ; // updated if answer from csp
uint8_t cacheex ; // updated if answer from cacheex
uint8_t localcards ; // updated if answer from local cards (or proxy using localcards option)
uint8_t proxy ; // updated if answer from local reader
uint32_t count ; // count of same cws receved
# ifdef CS_CACHEEX_AIO
uint8_t localgenerated ; // flag for local generated CWs
# endif
// for push out
pthread_rwlock_t pushout_client_lock ;
struct s_pushclient * pushout_client ; // list of clients that pushing cw
// end push out
node ht_node ; // node for hash table
node ll_node ; // node for linked list
} CW ;
typedef struct cache_t
{
hash_table ht_cw ;
list ll_cw ;
struct timeb upd_time ; // updated time. Update time at each cw got
struct timeb first_recv_time ; // time of first cw received
uint32_t csp_hash ;
node ht_node ; // node for hash table
node ll_node ; // node for linked list
} ECMHASH ;
# ifdef CS_CACHEEX_AIO
typedef struct cw_cache_t
{
uint8_t cw [ 16 ] ;
uint16_t caid ;
uint32_t prid ;
uint16_t srvid ;
struct timeb first_recv_time ; // time of first cw received
struct timeb upd_time ; // updated time. Update time at each cw got
node ht_node ;
node ll_node ;
} CW_CACHE ;
typedef struct cw_cache_setting_t
{
int8_t mode ;
uint16_t timediff_old_cw ;
} CW_CACHE_SETTING ;
# endif
static pthread_rwlock_t cache_lock ;
# ifdef CS_CACHEEX_AIO
static pthread_rwlock_t cw_cache_lock ;
# endif
static hash_table ht_cache ;
# ifdef CS_CACHEEX_AIO
static hash_table ht_cw_cache ;
# endif
static list ll_cache ;
# ifdef CS_CACHEEX_AIO
static list ll_cw_cache ;
# endif
static int8_t cache_init_done = 0 ;
# ifdef CS_CACHEEX_AIO
static int8_t cw_cache_init_done = 0 ;
static uint32_t lg_cache_size = 0 ;
void init_cw_cache ( void )
{
# ifdef CS_CACHEEX
if ( cfg . cw_cache_size > 0 | | cfg . cw_cache_memory > 0 )
{
init_hash_table ( & ht_cw_cache , & ll_cw_cache ) ;
if ( pthread_rwlock_init ( & cw_cache_lock , NULL ) ! = 0 )
{ cs_log ( " Error creating lock cw_cache_lock! " ) ; }
else
{ cw_cache_init_done = 1 ; }
}
# endif
}
# endif
void init_cache ( void )
{
init_hash_table ( & ht_cache , & ll_cache ) ;
if ( pthread_rwlock_init ( & cache_lock , NULL ) ! = 0 )
{ cs_log ( " Error creating lock cache_lock! " ) ; }
else
{ cache_init_done = 1 ; }
}
void free_cache ( void )
{
cleanup_cache ( true ) ;
# ifdef CS_CACHEEX_AIO
cw_cache_cleanup ( true ) ;
ecm_cache_cleanup ( true ) ;
cw_cache_init_done = 0 ;
deinitialize_hash_table ( & ht_cw_cache ) ;
pthread_rwlock_destroy ( & cw_cache_lock ) ;
# endif
cache_init_done = 0 ;
deinitialize_hash_table ( & ht_cache ) ;
pthread_rwlock_destroy ( & cache_lock ) ;
}
# ifdef CS_CACHEEX_AIO
uint32_t cache_size_lg ( void )
{
if ( ! cache_init_done )
{ return 0 ; }
return lg_cache_size ;
}
# endif
uint32_t cache_size ( void )
{
if ( ! cache_init_done )
{ return 0 ; }
return count_hash_table ( & ht_cache ) ;
}
static uint8_t count_sort ( CW * a , CW * b )
{
if ( a - > count = = b - > count ) return 0 ;
return ( a - > count > b - > count ) ? - 1 : 1 ; // DESC order by count
}
# ifdef CS_CACHEEX_AIO
static uint8_t time_sort ( CW_CACHE * a , CW_CACHE * b )
{
if ( ( ( int64_t ) ( a - > upd_time . time ) * 1000ull + ( int64_t ) a - > upd_time . millitm ) = = ( ( int64_t ) ( b - > upd_time . time ) * 1000ull + ( int64_t ) b - > upd_time . millitm ) ) return 0 ;
return ( ( ( int64_t ) ( a - > upd_time . time ) * 1000ull + ( int64_t ) a - > upd_time . millitm ) > ( ( int64_t ) ( b - > upd_time . time ) * 1000ull + ( int64_t ) b - > upd_time . millitm ) ) ? - 1 : 1 ;
}
# endif
uint8_t check_is_pushed ( void * cwp , struct s_client * cl )
{
struct s_pushclient * cl_tmp ;
CW * cw = ( CW * ) cwp ;
bool pushed = false ;
SAFE_RWLOCK_RDLOCK ( & cw - > pushout_client_lock ) ;
for ( cl_tmp = cw - > pushout_client ; cl_tmp ; cl_tmp = cl_tmp - > next_push )
{
if ( cl_tmp - > cl = = cl )
{
pushed = true ;
break ;
}
}
if ( ! pushed )
{
SAFE_RWLOCK_UNLOCK ( & cw - > pushout_client_lock ) ;
SAFE_RWLOCK_WRLOCK ( & cw - > pushout_client_lock ) ;
struct s_pushclient * new_push_client ;
if ( cs_malloc ( & new_push_client , sizeof ( struct s_pushclient ) ) )
{
new_push_client - > cl = cl ;
new_push_client - > next_push = cw - > pushout_client ;
cw - > pushout_client = new_push_client ;
}
SAFE_RWLOCK_UNLOCK ( & cw - > pushout_client_lock ) ;
return 0 ;
}
else
{
SAFE_RWLOCK_UNLOCK ( & cw - > pushout_client_lock ) ;
return 1 ;
}
}
uint8_t get_odd_even ( ECM_REQUEST * er )
{
return ( er - > ecm [ 0 ] ! = 0x80 & & er - > ecm [ 0 ] ! = 0x81 ? 0 : er - > ecm [ 0 ] ) ;
}
CW * get_first_cw ( ECMHASH * ecmhash , ECM_REQUEST * er )
{
if ( ! ecmhash ) return NULL ;
node * j ;
CW * cw ;
j = get_first_node_list ( & ecmhash - > ll_cw ) ;
while ( j ) {
cw = get_data_from_node ( j ) ;
if ( cw & & cw - > odd_even = = get_odd_even ( er ) & & ! cw - > got_bad_cwc )
return cw ;
j = j - > next ;
}
return NULL ;
}
int compare_csp_hash ( const void * arg , const void * obj )
{
uint32_t h = ( ( const ECMHASH * ) obj ) - > csp_hash ;
return memcmp ( arg , & h , 4 ) ;
}
static int compare_cw ( const void * arg , const void * obj )
{
return memcmp ( arg , ( ( const CW * ) obj ) - > cw , 16 ) ;
}
# ifdef CS_CACHEEX_AIO
static int compare_cw_cache ( const void * arg , const void * obj )
{
return memcmp ( arg , ( ( const CW_CACHE * ) obj ) - > cw , 16 ) ;
}
# endif
static bool cwcycle_check_cache ( struct s_client * cl , ECM_REQUEST * er , CW * cw )
{
( void ) cl ; ( void ) er ; ( void ) cw ;
# ifdef CW_CYCLE_CHECK
if ( cw - > got_bad_cwc )
return 0 ;
uint8_t cwc_ct = cw - > cwc_cycletime > 0 ? cw - > cwc_cycletime : 0 ;
uint8_t cwc_ncwc = cw - > cwc_next_cw_cycle < 2 ? cw - > cwc_next_cw_cycle : 2 ;
if ( checkcwcycle ( cl , er , NULL , cw - > cw , 0 , cwc_ct , cwc_ncwc ) ! = 0 )
{
cs_log_dbg ( D_CWC | D_LB , " {client %s, caid %04X, srvid %04X} [check_cache] cyclecheck passed ecm in INT. cache. " , ( cl ? cl - > account - > usr : " - " ) , er - > caid , er - > srvid ) ;
}
else
{
# ifdef CS_CACHEEX_AIO
if ( ! er - > localgenerated )
{
# endif
cs_log_dbg ( D_CWC , " cyclecheck [BAD CW Cycle] from Int. Cache detected.. {client %s, caid %04X, srvid %04X} [check_cache] -> skip cache answer " , ( cl ? cl - > account - > usr : " - " ) , er - > caid , er - > srvid ) ;
cw - > got_bad_cwc = 1 ; // no need to check it again
return 0 ;
# ifdef CS_CACHEEX_AIO
}
else
{
cs_log_dbg ( D_CWC , " cyclecheck [BAD CW Cycle] from Int. Cache detected.. {client %s, caid %04X, srvid %04X} [check_cache] -> lg-flagged CW -> do nothing " , ( cl ? cl - > account - > usr : " - " ) , er - > caid , er - > srvid ) ;
}
# endif
}
# endif
return 1 ;
}
/*
* This function returns cw ( mostly received ) in cache for er , or NULL if not found .
* IMPORTANT :
* - If found , DON ' T forget to free returned ecm , because it is a copy useful to get data
* - If found , and cacheex_src client of returned ecm is not NULL , and we want to access it ,
* remember to check for its validity ( client structure is still existent )
* E . g . : if ( ecm - > cacheex_src & & is_valid_client ( ecm - > cacheex_src ) & & ! ecm - > cacheex_src - > kill )
* We don ' t want make this stuff here to avoid useless cpu time if outside function we would not access to it .
*/
struct ecm_request_t * check_cache ( ECM_REQUEST * er , struct s_client * cl )
{
if ( ! cache_init_done | | ! er - > csp_hash ) return NULL ;
ECM_REQUEST * ecm = NULL ;
ECMHASH * result ;
CW * cw ;
uint64_t grp = cl ? cl - > grp : 0 ;
SAFE_RWLOCK_RDLOCK ( & cache_lock ) ;
result = find_hash_table ( & ht_cache , & er - > csp_hash , sizeof ( uint32_t ) , & compare_csp_hash ) ;
cw = get_first_cw ( result , er ) ;
if ( ! cw )
goto out_err ;
if ( cw - > csp // csp have no grp!
| | ! grp // csp client(no grp) searching for cache
| | ( grp & & cw - > grp // ecm group --> only when readers/ex-clients answer (e_found) it
& & ( grp & cw - > grp ) ) )
{
# ifdef CS_CACHEEX
//if preferlocalcards=2 for this ecm request, we can server ONLY cw from localcards readers until stage<3
if ( er - > preferlocalcards = = 2 & & ! cw - > localcards & & er - > stage < 3 ) {
goto out_err ;
}
CWCHECK check_cw = get_cwcheck ( er ) ;
if ( ( ! cw - > proxy & & ! cw - > localcards ) // cw received from ONLY cacheex/csp peers
& & check_cw . counter > 1
& & cw - > count < check_cw . counter
& & ( check_cw . mode = = 1 | | ! er - > cacheex_wait_time_expired ) )
{
goto out_err ;
}
# ifdef CS_CACHEEX_AIO
// client
if ( cl & & ! cw - > localgenerated
& & ! ( chk_srvid_localgenerated_only_exception ( er ) ) // service-based exception
& & ( cl - > account - > cacheex . localgenerated_only
| | ( chk_lg_only ( er , & cl - > account - > cacheex . lg_only_tab ) )
) // only lg-flagged CWs
)
{
goto out_err ;
}
# endif
# endif
if ( ! cwcycle_check_cache ( cl , er , cw ) )
goto out_err ;
if ( cs_malloc ( & ecm , sizeof ( ECM_REQUEST ) ) )
{
ecm - > rc = E_FOUND ;
ecm - > rcEx = 0 ;
memcpy ( ecm - > cw , cw - > cw , 16 ) ;
ecm - > grp = cw - > grp ;
ecm - > selected_reader = cw - > selected_reader ;
ecm - > cwc_cycletime = cw - > cwc_cycletime ;
ecm - > cwc_next_cw_cycle = cw - > cwc_next_cw_cycle ;
ecm - > cacheex_src = cw - > cacheex_src ;
# ifdef CS_CACHEEX_AIO
ecm - > localgenerated = ( cw - > localgenerated ) ? 1 : 0 ;
# endif
ecm - > cw_count = cw - > count ;
ecm - > from_cacheex = cw - > cacheex ; // Kopiowanie flagi cacheex
ecm - > from_csp = cw - > csp ; // Kopiowanie flagi csp
}
}
out_err :
SAFE_RWLOCK_UNLOCK ( & cache_lock ) ;
return ecm ;
}
# ifdef CS_CACHEEX_AIO
uint16_t get_cacheex_nopushafter ( ECM_REQUEST * er )
{
return caidvaluetab_get_value ( & cfg . cacheex_nopushafter_tab , er - > caid , 0 ) ;
}
# endif
static void cacheex_cache_add ( ECM_REQUEST * er , ECMHASH * result , CW * cw , bool add_new_cw )
{
( void ) er ; ( void ) result ; ( void ) cw ; ( void ) add_new_cw ;
# ifdef CS_CACHEEX
er - > cw_cache = cw ;
cacheex_cache_push ( er ) ;
// cacheex debug log lines and cw diff stuff
if ( ! check_client ( er - > cacheex_src ) )
return ;
# ifdef CS_CACHEEX_AIO
if ( D_CACHEEX & cs_dblevel )
{
uint8_t remotenodeid [ 8 ] ;
cacheex_get_srcnodeid ( er , remotenodeid ) ;
if ( ! add_new_cw )
{
debug_ecm ( D_CACHEEX | D_CSP , " got duplicate pushed ECM %s from %s - hop %i %s, src-nodeid % " PRIu64 " X " , buf , er - > from_csp ? " csp " : username ( er - > cacheex_src ) , ll_count ( er - > csp_lastnodes ) , er - > localgenerated ? " (lg) " : " " , cacheex_node_id ( remotenodeid ) ) ;
return ;
}
debug_ecm ( D_CACHEEX | D_CSP , " got pushed ECM %s from %s - hop %i %s, src-nodeid % " PRIu64 " X " , buf , er - > from_csp ? " csp " : username ( er - > cacheex_src ) , ll_count ( er - > csp_lastnodes ) , er - > localgenerated ? " (lg) " : " " , cacheex_node_id ( remotenodeid ) ) ;
}
else
{
# endif
if ( ! add_new_cw )
{
# ifdef CS_CACHEEX_AIO
debug_ecm ( D_CACHEEX | D_CSP , " got duplicate pushed ECM %s from %s - hop %i %s " , buf , er - > from_csp ? " csp " : username ( er - > cacheex_src ) , ll_count ( er - > csp_lastnodes ) , er - > localgenerated ? " (lg) " : " " ) ;
# else
debug_ecm ( D_CACHEEX | D_CSP , " got duplicate pushed ECM %s from %s " , buf , er - > from_csp ? " csp " : username ( er - > cacheex_src ) ) ;
# endif
return ;
}
# ifdef CS_CACHEEX_AIO
debug_ecm ( D_CACHEEX | D_CSP , " got pushed ECM %s from %s - hop %i %s " , buf , er - > from_csp ? " csp " : username ( er - > cacheex_src ) , ll_count ( er - > csp_lastnodes ) , er - > localgenerated ? " (lg) " : " " ) ;
# else
debug_ecm ( D_CACHEEX | D_CSP , " got pushed ECM %s from %s " , buf , er - > from_csp ? " csp " : username ( er - > cacheex_src ) ) ;
# endif
# ifdef CS_CACHEEX_AIO
}
# endif
CW * cw_first = get_first_cw ( result , er ) ;
if ( ! cw_first )
return ;
// compare er cw with mostly counted cached cw
if ( memcmp ( er - > cw , cw_first - > cw , sizeof ( er - > cw ) ) ! = 0 )
{
er - > cacheex_src - > cwcacheexerrcw + + ;
if ( er - > cacheex_src - > account )
er - > cacheex_src - > account - > cwcacheexerrcw + + ;
if ( ( ( 0x0200 | 0x0800 ) & cs_dblevel ) ) // avoid useless operations if debug is not enabled
{
char cw1 [ 16 * 3 + 2 ] , cw2 [ 16 * 3 + 2 ] ;
cs_hexdump ( 0 , er - > cw , 16 , cw1 , sizeof ( cw1 ) ) ;
cs_hexdump ( 0 , cw_first - > cw , 16 , cw2 , sizeof ( cw2 ) ) ;
char ip1 [ 20 ] = " " , ip2 [ 20 ] = " " ;
if ( check_client ( er - > cacheex_src ) )
cs_strncpy ( ip1 , cs_inet_ntoa ( er - > cacheex_src - > ip ) , sizeof ( ip1 ) ) ;
if ( check_client ( cw_first - > cacheex_src ) )
cs_strncpy ( ip2 , cs_inet_ntoa ( cw_first - > cacheex_src - > ip ) , sizeof ( ip2 ) ) ;
else if ( cw_first - > selected_reader & & check_client ( cw_first - > selected_reader - > client ) )
cs_strncpy ( ip2 , cs_inet_ntoa ( cw_first - > selected_reader - > client - > ip ) , sizeof ( ip2 ) ) ;
# ifdef CS_CACHEEX_AIO
uint8_t remotenodeid [ 8 ] ;
cacheex_get_srcnodeid ( er , remotenodeid ) ;
uint8_t fakeF0 = 0 , offset = 0 ;
if ( get_odd_even ( er ) = = 0x81 )
offset = 8 ;
if (
( cw_first - > cw [ 7 + offset ] ! = 0x00 & & er - > cw [ 7 + offset ] ! = 0x00 )
& & ( cw_first - > cw [ 7 + offset ] ^ 0xF0 ) = = er - > cw [ 7 + offset ]
)
{
fakeF0 = 1 ;
}
debug_ecm ( D_CACHEEX | D_CSP , " WARNING: Different CWs %s from %s(%s)<>%s(%s): %s<>%s lg: %i<>%i, hop:%02i, src-nodeid: % " PRIu64 " X%s " , buf ,
# else
debug_ecm ( D_CACHEEX | D_CSP , " WARNING: Different CWs %s from %s(%s)<>%s(%s): %s<>%s " , buf ,
# endif
er - > from_csp ? " csp " : username ( er - > cacheex_src ) , ip1 ,
check_client ( cw_first - > cacheex_src ) ? username ( cw_first - > cacheex_src ) : ( cw_first - > selected_reader ? cw_first - > selected_reader - > label : " unknown/csp " ) , ip2 ,
# ifdef CS_CACHEEX_AIO
cw1 , cw2 , er - > localgenerated , cw_first - > localgenerated , er - > csp_lastnodes ? ll_count ( er - > csp_lastnodes ) : 0 , er - > csp_lastnodes ? cacheex_node_id ( remotenodeid ) : 0 , fakeF0 ? " [last byte xor 0xF0] " : " " ) ;
# else
cw1 , cw2 ) ;
# endif
# ifdef WITH_DEBUG
if ( cs_dblevel & D_CACHEEX )
{
LL_LOCKITER * li = ll_li_create ( er - > csp_lastnodes , 0 ) ;
uint8_t * nodeid ;
uint8_t hops = 0 ;
while ( ( nodeid = ll_li_next ( li ) ) )
{
cs_log_dbg ( D_CACHEEX , " Different CW-nodelist hop%02u: % " PRIu64 " X " , + + hops , cacheex_node_id ( nodeid ) ) ;
}
ll_li_destroy ( li ) ;
}
# endif
}
}
# endif
}
# ifdef CS_CACHEEX_AIO
CW_CACHE_SETTING get_cw_cache ( ECM_REQUEST * er )
{
int32_t i , timediff_old_cw = 0 ;
int8_t mode = 0 ;
for ( i = 0 ; i < cfg . cw_cache_settings . cwchecknum ; i + + )
{
CWCHECKTAB_DATA * d = & cfg . cw_cache_settings . cwcheckdata [ i ] ;
if ( i = = 0 & & d - > caid < = 0 )
{
mode = d - > mode ;
timediff_old_cw = d - > counter ;
continue ; //check other, only valid for unset
}
if ( d - > caid = = er - > caid | | d - > caid = = er - > caid > > 8 | | ( ( d - > cmask > = 0 & & ( er - > caid & d - > cmask ) = = d - > caid ) | | d - > caid = = - 1 ) )
{
if ( ( d - > prid > = 0 & & d - > prid = = ( int32_t ) er - > prid ) | | d - > prid = = - 1 )
{
if ( ( d - > srvid > = 0 & & d - > srvid = = er - > srvid ) | | d - > srvid = = - 1 )
{
mode = d - > mode ;
timediff_old_cw = d - > counter ;
break ;
}
}
}
}
//check for correct values
if ( mode > 3 | | mode < 0 ) mode = 0 ;
if ( timediff_old_cw < 1 ) timediff_old_cw = 0 ;
CW_CACHE_SETTING cw_cache_setting ;
memset ( & cw_cache_setting , 0 , sizeof ( CW_CACHE_SETTING ) ) ;
cw_cache_setting . mode = mode ;
cw_cache_setting . timediff_old_cw = timediff_old_cw ;
return cw_cache_setting ;
}
2026-02-23 16:40:08 +00:00
static bool cw_cache_check ( ECM_REQUEST * er )
2026-02-16 09:02:48 +00:00
{
if ( cw_cache_init_done )
{
CW_CACHE_SETTING cw_cache_setting = get_cw_cache ( er ) ;
if ( cw_cache_setting . mode > 0 )
{
CW_CACHE * cw_cache = NULL ;
SAFE_RWLOCK_WRLOCK ( & cw_cache_lock ) ;
cw_cache = find_hash_table ( & ht_cw_cache , & er - > cw , sizeof ( er - > cw ) , & compare_cw_cache ) ;
// add cw to ht_cw_cache if < cw_cache_size
if ( ! cw_cache )
{
// cw_cache-size(count/memory) pre-check
if (
( cfg . cw_cache_size & & ( cfg . cw_cache_size > tommy_hashlin_count ( & ht_cw_cache ) ) )
| | ( cfg . cw_cache_memory & & ( cfg . cw_cache_memory * 1024 * 1024 > ( 2 * tommy_hashlin_memory_usage ( & ht_cw_cache ) ) ) )
)
{
if ( cs_malloc ( & cw_cache , sizeof ( CW_CACHE ) ) )
{
memcpy ( cw_cache - > cw , er - > cw , sizeof ( er - > cw ) ) ;
cw_cache - > caid = er - > caid ;
cw_cache - > prid = er - > prid ;
cw_cache - > srvid = er - > srvid ;
cs_ftime ( & cw_cache - > first_recv_time ) ;
cs_ftime ( & cw_cache - > upd_time ) ;
tommy_hashlin_insert ( & ht_cw_cache , & cw_cache - > ht_node , cw_cache , tommy_hash_u32 ( 0 , & er - > cw , sizeof ( er - > cw ) ) ) ;
tommy_list_insert_tail ( & ll_cw_cache , & cw_cache - > ll_node , cw_cache ) ;
SAFE_RWLOCK_UNLOCK ( & cw_cache_lock ) ;
return true ;
}
else
{
SAFE_RWLOCK_UNLOCK ( & cw_cache_lock ) ;
cs_log ( " [cw_cache] ERROR: NO added HASH to cw_cache!! " ) ;
return false ;
}
}
else
{
// clean cache call;
SAFE_RWLOCK_UNLOCK ( & cw_cache_lock ) ;
cw_cache_cleanup ( false ) ;
return false ;
}
}
// cw found
else
{
char cw1 [ 16 * 3 + 2 ] ;
char cw2 [ 16 * 3 + 2 ] ;
int8_t drop_cw = 0 ;
int64_t gone_diff = 0 ;
gone_diff = comp_timeb ( & er - > tps , & cw_cache - > first_recv_time ) ;
if ( D_CW_CACHE & cs_dblevel )
{
cs_hexdump ( 0 , cw_cache - > cw , 16 , cw1 , sizeof ( cw1 ) ) ;
cs_hexdump ( 0 , er - > cw , 16 , cw2 , sizeof ( cw2 ) ) ;
}
if ( cw_cache_setting . timediff_old_cw > 0 & & gone_diff > cw_cache_setting . timediff_old_cw ) // late (>cw_cache_setting.timediff_old_cw) cw incoming
{
// log every dupe cw
if ( cs_dblevel & D_CW_CACHE )
{
uint8_t remotenodeid [ 8 ] ;
cacheex_get_srcnodeid ( er , remotenodeid ) ;
cs_log_dbg ( D_CW_CACHE , " [dupe CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s | diff(now): % " PRIi64 " ms > % " PRIu16 " - %s - hop %i%s, src-nodeid % " PRIu64 " X " , cw_cache - > caid , cw_cache - > prid , cw_cache - > srvid , cw1 , er - > caid , er - > prid , er - > srvid , cw2 , gone_diff , cw_cache_setting . timediff_old_cw , ( er - > selected_reader & & cs_strlen ( er - > selected_reader - > label ) ) ? er - > selected_reader - > label : username ( er - > cacheex_src ) , ll_count ( er - > csp_lastnodes ) , ( er - > localgenerated ) ? " (lg) " : " " , er - > csp_lastnodes ? cacheex_node_id ( remotenodeid ) : 0 ) ;
}
if ( cw_cache - > srvid = = er - > srvid & & cw_cache - > caid = = er - > caid ) // same cw for same caid&srvid
{
cs_ftime ( & cw_cache - > upd_time ) ;
cs_log_dbg ( D_CW_CACHE , " [late CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s | diff(now): % " PRIi64 " ms > % " PRIu16 " - %s - hop %i%s " , cw_cache - > caid , cw_cache - > prid , cw_cache - > srvid , cw1 , er - > caid , er - > prid , er - > srvid , cw2 , gone_diff , cw_cache_setting . timediff_old_cw , ( er - > selected_reader & & cs_strlen ( er - > selected_reader - > label ) ) ? er - > selected_reader - > label : username ( er - > cacheex_src ) , ll_count ( er - > csp_lastnodes ) , ( er - > localgenerated ) ? " (lg) " : " " ) ;
drop_cw = 1 ;
}
else if ( cw_cache - > srvid ! = er - > srvid ) // same cw for different srvid & late
{
cs_ftime ( & cw_cache - > upd_time ) ;
cs_log_dbg ( D_CW_CACHE , " [dupe&late CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s| diff(now): % " PRIi64 " ms - %s - hop %i%s " , cw_cache - > caid , cw_cache - > prid , cw_cache - > srvid , cw1 , er - > caid , er - > prid , er - > srvid , cw2 , gone_diff , ( er - > selected_reader & & cs_strlen ( er - > selected_reader - > label ) ) ? er - > selected_reader - > label : username ( er - > cacheex_src ) , ll_count ( er - > csp_lastnodes ) , ( er - > localgenerated ) ? " (lg) " : " " ) ;
drop_cw = 1 ;
}
else if ( gone_diff > 15000 ) // same cw later as 15 secs
{
# ifdef WITH_DEBUG
if ( cs_dblevel & D_CW_CACHE )
{
uint8_t remotenodeid [ 8 ] ;
cacheex_get_srcnodeid ( er , remotenodeid ) ;
cs_log_dbg ( D_CW_CACHE , " [late-15sec+ CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s | diff(now): % " PRIi64 " ms > % " PRIu16 " - %s - hop %i%s, src-nodeid % " PRIu64 " X " , cw_cache - > caid , cw_cache - > prid , cw_cache - > srvid , cw1 , er - > caid , er - > prid , er - > srvid , cw2 , gone_diff , cw_cache_setting . timediff_old_cw , ( er - > selected_reader & & cs_strlen ( er - > selected_reader - > label ) ) ? er - > selected_reader - > label : username ( er - > cacheex_src ) , ll_count ( er - > csp_lastnodes ) , ( er - > localgenerated ) ? " (lg) " : " " , er - > csp_lastnodes ? cacheex_node_id ( remotenodeid ) : 0 ) ;
}
# endif
drop_cw = 1 ;
}
if ( cw_cache_setting . mode > 1 & & drop_cw )
{
// cw_cache->drop_count++;
cs_log_dbg ( D_CW_CACHE , " incoming CW dropped - current cw_cache_size: %i - cw_cache-mem-size: %iMiB " , count_hash_table ( & ht_cw_cache ) , 2 * ( int ) tommy_hashlin_memory_usage ( & ht_cw_cache ) / 1024 / 1024 ) ;
SAFE_RWLOCK_UNLOCK ( & cw_cache_lock ) ;
return false ;
}
}
}
SAFE_RWLOCK_UNLOCK ( & cw_cache_lock ) ;
return true ;
}
}
else
{
cs_log_dbg ( D_CW_CACHE , " [cw_cache] cw_cache_init_done %i cfg.cw_cache_size: %u cfg.cw_cache_memory %u " , cw_cache_init_done , cfg . cw_cache_size , cfg . cw_cache_memory ) ;
return true ;
}
return true ;
}
# endif
void add_cache ( ECM_REQUEST * er )
{
if ( ! cache_init_done | | ! er - > csp_hash ) return ;
2026-02-17 10:06:53 +00:00
# ifdef CS_CACHEEX
if ( cfg . cacheex_global_maxhop )
{
int8_t hop = 0 ;
if ( er - > cacheex_src )
{
hop = er - > cacheex_src - > cacheex_maxhop ;
}
uint16_t percaid_maxhop = caidvaluetab_get_value (
& cfg . cacheex_maxhop_percaid ,
er - > caid ,
cfg . cacheex_maxhop
) ;
int8_t maxhop = ( int8_t ) percaid_maxhop ;
if ( hop > maxhop )
{
cs_log_dbg ( D_CACHEEX ,
" cacheex: DROP hop %d > maxhop %d (caid %04X) " ,
hop , maxhop , er - > caid ) ;
return ;
}
}
2026-02-16 09:02:48 +00:00
# endif
ECMHASH * result = NULL ;
CW * cw = NULL ;
bool add_new_cw = false ;
SAFE_RWLOCK_WRLOCK ( & cache_lock ) ;
// add csp_hash to cache
result = find_hash_table ( & ht_cache , & er - > csp_hash , sizeof ( uint32_t ) , & compare_csp_hash ) ;
if ( ! result )
{
if ( cs_malloc ( & result , sizeof ( ECMHASH ) ) )
{
result - > csp_hash = er - > csp_hash ;
init_hash_table ( & result - > ht_cw , & result - > ll_cw ) ;
cs_ftime ( & result - > first_recv_time ) ;
add_hash_table ( & ht_cache , & result - > ht_node , & ll_cache , & result - > ll_node , result , & result - > csp_hash , sizeof ( uint32_t ) ) ;
}
else
{
SAFE_RWLOCK_UNLOCK ( & cache_lock ) ;
cs_log ( " ERROR: NO added HASH to cache!! " ) ;
return ;
}
}
cs_ftime ( & result - > upd_time ) ; // need to be updated at each cw! We use it for deleting this hash when no more cws arrive inside max_cache_time!
//add cw to this csp hash
cw = find_hash_table ( & result - > ht_cw , er - > cw , sizeof ( er - > cw ) , & compare_cw ) ;
if ( ! cw )
{
if ( count_hash_table ( & result - > ht_cw ) > = 10 ) // max 10 different cws stored
{
SAFE_RWLOCK_UNLOCK ( & cache_lock ) ;
return ;
}
while ( 1 )
{
if ( cs_malloc ( & cw , sizeof ( CW ) ) )
{
memcpy ( cw - > cw , er - > cw , sizeof ( er - > cw ) ) ;
cw - > odd_even = get_odd_even ( er ) ;
cw - > cwc_cycletime = er - > cwc_cycletime ;
cw - > cwc_next_cw_cycle = er - > cwc_next_cw_cycle ;
cw - > count = 0 ;
cw - > csp = 0 ;
cw - > cacheex = 0 ;
cw - > localcards = 0 ;
cw - > proxy = 0 ;
cw - > grp = 0 ;
cw - > caid = er - > caid ;
cw - > prid = er - > prid ;
cw - > srvid = er - > srvid ;
cw - > selected_reader = er - > selected_reader ;
cw - > cacheex_src = er - > cacheex_src ;
cw - > pushout_client = NULL ;
while ( 1 )
{
if ( pthread_rwlock_init ( & cw - > pushout_client_lock , NULL ) = = 0 )
break ;
cs_log ( " Error creating lock pushout_client_lock! " ) ;
cs_sleepms ( 1 ) ;
}
add_hash_table ( & result - > ht_cw , & cw - > ht_node , & result - > ll_cw , & cw - > ll_node , cw , cw - > cw , sizeof ( er - > cw ) ) ;
add_new_cw = true ;
break ;
}
cs_log ( " ERROR: NO added CW to cache!! Re-trying... " ) ;
cs_sleepms ( 1 ) ;
}
}
// update if answered from csp/cacheex/local_proxy
if ( er - > from_cacheex ) cw - > cacheex = 1 ;
if ( er - > from_csp ) cw - > csp = 1 ;
if ( ! er - > cacheex_src )
{
if ( is_localreader ( er - > selected_reader , er ) ) cw - > localcards = 1 ;
else cw - > proxy = 1 ;
}
# ifdef CS_CACHEEX_AIO
// copy flag for local generated CW
if ( er - > localgenerated | | ( er - > selected_reader & & ! is_network_reader ( er - > selected_reader ) ) )
{
cw - > localgenerated = 1 ;
er - > localgenerated = 1 ;
// to favorite CWs with this flag while sorting
if ( cw - > count < 0x0F000000 )
{
cw - > count | = 0x0F000000 ;
lg_cache_size + + ;
}
}
else
{
cw - > localgenerated = 0 ;
}
# endif
// always update group and counter
cw - > grp | = er - > grp ;
cw - > count + + ;
# ifdef CS_CACHEEX_AIO
// add count to er for checking @ cacheex_push
er - > cw_count + = cw - > count ;
# endif
// sort cw_list by counter (DESC order)
if ( cw - > count > 1 )
sort_list ( & result - > ll_cw , count_sort ) ;
# ifdef CS_CACHEEX_AIO
// dont push not flagged CWs - global
if ( ! er - > localgenerated & &
(
! chk_srvid_localgenerated_only_exception ( er )
& & ( cfg . cacheex_localgenerated_only | | chk_lg_only ( er , & cfg . cacheex_lg_only_tab ) )
) )
{
cs_log_dbg ( D_CACHEEX , " cacheex: push denied, cacheex_localgenerated_only->global " ) ;
SAFE_RWLOCK_UNLOCK ( & cache_lock ) ;
return ;
}
// dont push CW if time for caid > x && from local reader | proxy
if ( er - > rc < 3 & & er - > ecm_time & & get_cacheex_nopushafter ( er ) ! = 0 & & ( get_cacheex_nopushafter ( er ) < er - > ecm_time ) )
{
cs_log_dbg ( D_CACHEEX , " cacheex: push denied, cacheex_nopushafter %04X:%u < %i, reader: %s " , er - > caid , get_cacheex_nopushafter ( er ) , er - > ecm_time , er - > selected_reader - > label ) ;
SAFE_RWLOCK_UNLOCK ( & cache_lock ) ;
return ;
}
// no cacheex-push on diff-cw's if no localgenerated flag exist
if ( cfg . cacheex_dropdiffs & & ( count_hash_table ( & result - > ht_cw ) > 1 ) & & ! er - > localgenerated )
{
cs_log_dbg ( D_CACHEEX , " cacheex: diff CW - cacheex push denied src: %s " , er - > selected_reader - > label ) ;
SAFE_RWLOCK_UNLOCK ( & cache_lock ) ;
return ;
}
# endif
SAFE_RWLOCK_UNLOCK ( & cache_lock ) ;
cacheex_cache_add ( er , result , cw , add_new_cw ) ;
}
# ifdef CS_CACHEEX_AIO
void cw_cache_cleanup ( bool force )
{
if ( ! cw_cache_init_done )
{ return ; }
SAFE_RWLOCK_WRLOCK ( & cw_cache_lock ) ;
CW_CACHE * cw_cache ;
node * i , * i_next ;
uint32_t ll_c = 0 ;
uint32_t ll_ten_percent = ( uint ) tommy_list_count ( & ll_cw_cache ) * 0.1 ; // 10 percent of cache
if ( ! force )
sort_list ( & ll_cw_cache , time_sort ) ;
i = get_first_node_list ( & ll_cw_cache ) ;
while ( i )
{
i_next = i - > next ;
cw_cache = get_data_from_node ( i ) ;
if ( ! cw_cache )
{
i = i_next ;
continue ;
}
if ( ! force )
{
+ + ll_c ;
if ( ll_c < ll_ten_percent )
{
remove_elem_list ( & ll_cw_cache , & cw_cache - > ll_node ) ;
remove_elem_hash_table ( & ht_cw_cache , & cw_cache - > ht_node ) ;
NULLFREE ( cw_cache ) ;
}
else {
break ;
}
}
else
{
remove_elem_list ( & ll_cw_cache , & cw_cache - > ll_node ) ;
remove_elem_hash_table ( & ht_cw_cache , & cw_cache - > ht_node ) ;
NULLFREE ( cw_cache ) ;
}
i = i_next ;
}
SAFE_RWLOCK_UNLOCK ( & cw_cache_lock ) ;
}
# endif
void cleanup_cache ( bool force )
{
ECMHASH * ecmhash ;
CW * cw ;
struct s_pushclient * pc , * nxt ;
node * i , * i_next , * j , * j_next ;
struct timeb now ;
int64_t gone_first , gone_upd ;
if ( ! cache_init_done )
{ return ; }
SAFE_RWLOCK_WRLOCK ( & cache_lock ) ;
i = get_first_node_list ( & ll_cache ) ;
while ( i )
{
i_next = i - > next ;
ecmhash = get_data_from_node ( i ) ;
if ( ! ecmhash )
{
i = i_next ;
continue ;
}
cs_ftime ( & now ) ;
gone_first = comp_timeb ( & now , & ecmhash - > first_recv_time ) ;
gone_upd = comp_timeb ( & now , & ecmhash - > upd_time ) ;
if ( ! force & & gone_first < = ( cfg . max_cache_time * 1000 ) ) // not continue, useless check for nexts one!
{
break ;
}
if ( force | | gone_upd > ( cfg . max_cache_time * 1000 ) )
{
j = get_first_node_list ( & ecmhash - > ll_cw ) ;
while ( j )
{
j_next = j - > next ;
cw = get_data_from_node ( j ) ;
if ( cw )
{
pthread_rwlock_destroy ( & cw - > pushout_client_lock ) ;
pc = cw - > pushout_client ;
cw - > pushout_client = NULL ;
while ( pc )
{
nxt = pc - > next_push ;
NULLFREE ( pc ) ;
pc = nxt ;
}
# ifdef CS_CACHEEX_AIO
if ( cw - > count > = 0x0F000000 )
{
lg_cache_size - - ;
}
# endif
remove_elem_list ( & ecmhash - > ll_cw , & cw - > ll_node ) ;
remove_elem_hash_table ( & ecmhash - > ht_cw , & cw - > ht_node ) ;
NULLFREE ( cw ) ;
}
j = j_next ;
}
deinitialize_hash_table ( & ecmhash - > ht_cw ) ;
remove_elem_list ( & ll_cache , & ecmhash - > ll_node ) ;
remove_elem_hash_table ( & ht_cache , & ecmhash - > ht_node ) ;
NULLFREE ( ecmhash ) ;
}
i = i_next ;
}
SAFE_RWLOCK_UNLOCK ( & cache_lock ) ;
}
# ifdef CS_CACHEEX_AIO
void cacheex_get_srcnodeid ( ECM_REQUEST * er , uint8_t * remotenodeid )
{
uint8_t * data ;
data = ll_last_element ( er - > csp_lastnodes ) ;
if ( data )
{
memcpy ( remotenodeid , data , 8 ) ;
}
else
{
memset ( remotenodeid , 0 , 8 ) ;
}
}
# endif