fixed CMake warnings on Debian 11
[oscam.git] / oscam-cache.c
blobab1b202cb58670a7c7092f56e623c0ab2a59ba3e
1 #define MODULE_LOG_PREFIX "cache"
3 #include "globals.h"
4 #include "module-cacheex.h"
5 #include "module-cw-cycle-check.h"
6 #include "oscam-cache.h"
7 #include "oscam-chk.h"
8 #include "oscam-client.h"
9 #include "oscam-ecm.h"
10 #include "oscam-garbage.h"
11 #include "oscam-lock.h"
12 #include "oscam-net.h"
13 #include "oscam-string.h"
14 #include "oscam-time.h"
15 #include "oscam-hashtable.h"
16 #ifdef CS_CACHEEX_AIO
17 #include "oscam-log.h"
18 #endif
21 // CACHE functions **************************************************************+
22 struct s_pushclient
24 struct s_client *cl;
25 struct s_pushclient *next_push;
28 typedef struct cw_t
30 uint8_t cw[16];
31 uint8_t odd_even; // odd/even byte (0x80 0x81)
32 uint8_t cwc_cycletime;
33 uint8_t cwc_next_cw_cycle;
34 uint8_t got_bad_cwc; // used by cycle check
35 uint16_t caid; // first caid received
36 uint32_t prid; // first prid received
37 uint16_t srvid; // first srvid received
38 struct s_reader *selected_reader; // first answering: reader
39 struct s_client *cacheex_src; // first answering: cacheex client
40 uint64_t grp; // updated grp
41 uint8_t csp; // updated if answer from csp
42 uint8_t cacheex; // updated if answer from cacheex
43 uint8_t localcards; // updated if answer from local cards (or proxy using localcards option)
44 uint8_t proxy; // updated if answer from local reader
45 uint32_t count; // count of same cws receved
46 #ifdef CS_CACHEEX_AIO
47 uint8_t localgenerated; // flag for local generated CWs
48 #endif
49 // for push out
50 pthread_rwlock_t pushout_client_lock;
51 struct s_pushclient *pushout_client; // list of clients that pushing cw
52 // end push out
53 node ht_node; // node for hash table
54 node ll_node; // node for linked list
55 } CW;
57 typedef struct cache_t
59 hash_table ht_cw;
60 list ll_cw;
61 struct timeb upd_time; // updated time. Update time at each cw got
62 struct timeb first_recv_time; // time of first cw received
63 uint32_t csp_hash;
64 node ht_node; // node for hash table
65 node ll_node; // node for linked list
66 } ECMHASH;
68 #ifdef CS_CACHEEX_AIO
69 typedef struct cw_cache_t
71 uint8_t cw[16];
72 uint16_t caid;
73 uint32_t prid;
74 uint16_t srvid;
75 struct timeb first_recv_time; // time of first cw received
76 struct timeb upd_time; // updated time. Update time at each cw got
77 node ht_node;
78 node ll_node;
79 } CW_CACHE;
81 typedef struct cw_cache_setting_t
83 int8_t mode;
84 uint16_t timediff_old_cw;
85 } CW_CACHE_SETTING;
86 #endif
88 static pthread_rwlock_t cache_lock;
89 #ifdef CS_CACHEEX_AIO
90 static pthread_rwlock_t cw_cache_lock;
91 #endif
92 static hash_table ht_cache;
93 #ifdef CS_CACHEEX_AIO
94 static hash_table ht_cw_cache;
95 #endif
96 static list ll_cache;
97 #ifdef CS_CACHEEX_AIO
98 static list ll_cw_cache;
99 #endif
100 static int8_t cache_init_done = 0;
102 #ifdef CS_CACHEEX_AIO
103 static int8_t cw_cache_init_done = 0;
104 static uint32_t lg_cache_size = 0;
106 void init_cw_cache(void)
108 #ifdef CS_CACHEEX
109 if(cfg.cw_cache_size > 0 || cfg.cw_cache_memory > 0)
111 init_hash_table(&ht_cw_cache, &ll_cw_cache);
112 if (pthread_rwlock_init(&cw_cache_lock,NULL) != 0)
113 { cs_log("Error creating lock cw_cache_lock!"); }
114 else
115 { cw_cache_init_done = 1; }
117 #endif
119 #endif
121 void init_cache(void)
123 init_hash_table(&ht_cache, &ll_cache);
124 if (pthread_rwlock_init(&cache_lock,NULL) != 0)
125 { cs_log("Error creating lock cache_lock!"); }
126 else
127 { cache_init_done = 1; }
130 void free_cache(void)
132 cleanup_cache(true);
133 #ifdef CS_CACHEEX_AIO
134 cw_cache_cleanup(true);
135 ecm_cache_cleanup(true);
136 cw_cache_init_done = 0;
137 deinitialize_hash_table(&ht_cw_cache);
138 pthread_rwlock_destroy(&cw_cache_lock);
139 #endif
140 cache_init_done = 0;
141 deinitialize_hash_table(&ht_cache);
142 pthread_rwlock_destroy(&cache_lock);
145 #ifdef CS_CACHEEX_AIO
146 uint32_t cache_size_lg(void)
148 if(!cache_init_done)
149 { return 0; }
151 return lg_cache_size;
153 #endif
155 uint32_t cache_size(void)
157 if(!cache_init_done)
158 { return 0; }
160 return count_hash_table(&ht_cache);
163 static uint8_t count_sort(CW *a, CW *b)
165 if (a->count == b->count) return 0;
166 return (a->count > b->count) ? -1 : 1; // DESC order by count
169 #ifdef CS_CACHEEX_AIO
170 static uint8_t time_sort(CW_CACHE *a, CW_CACHE *b)
172 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;
173 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;
175 #endif
177 uint8_t check_is_pushed(void *cwp, struct s_client *cl)
179 struct s_pushclient *cl_tmp;
180 CW* cw = (CW*)cwp;
181 bool pushed=false;
183 SAFE_RWLOCK_RDLOCK(&cw->pushout_client_lock);
184 for (cl_tmp = cw->pushout_client; cl_tmp; cl_tmp = cl_tmp->next_push)
186 if(cl_tmp->cl==cl)
188 pushed=true;
189 break;
193 if(!pushed)
195 SAFE_RWLOCK_UNLOCK(&cw->pushout_client_lock);
196 SAFE_RWLOCK_WRLOCK(&cw->pushout_client_lock);
198 struct s_pushclient *new_push_client;
199 if(cs_malloc(&new_push_client, sizeof(struct s_pushclient)))
201 new_push_client->cl=cl;
203 new_push_client->next_push=cw->pushout_client;
204 cw->pushout_client=new_push_client;
207 SAFE_RWLOCK_UNLOCK(&cw->pushout_client_lock);
208 return 0;
210 else
212 SAFE_RWLOCK_UNLOCK(&cw->pushout_client_lock);
213 return 1;
217 uint8_t get_odd_even(ECM_REQUEST *er)
219 return (er->ecm[0] != 0x80 && er->ecm[0] != 0x81 ? 0 : er->ecm[0]);
223 CW *get_first_cw(ECMHASH *ecmhash, ECM_REQUEST *er)
225 if(!ecmhash) return NULL;
227 node *j;
228 CW *cw;
230 j = get_first_node_list(&ecmhash->ll_cw);
231 while (j) {
232 cw = get_data_from_node(j);
234 if(cw && cw->odd_even == get_odd_even(er) && !cw->got_bad_cwc)
235 return cw;
237 j = j->next;
240 return NULL;
243 int compare_csp_hash(const void *arg, const void *obj)
245 uint32_t h = ((const ECMHASH*)obj)->csp_hash;
246 return memcmp(arg, &h, 4);
249 static int compare_cw(const void *arg, const void *obj)
251 return memcmp(arg, ((const CW*)obj)->cw, 16);
254 #ifdef CS_CACHEEX_AIO
255 static int compare_cw_cache(const void *arg, const void *obj)
257 return memcmp(arg, ((const CW_CACHE*)obj)->cw, 16);
259 #endif
261 static bool cwcycle_check_cache(struct s_client *cl, ECM_REQUEST *er, CW *cw)
263 (void)cl; (void)er; (void)cw;
265 #ifdef CW_CYCLE_CHECK
266 if(cw->got_bad_cwc)
267 return 0;
269 uint8_t cwc_ct = cw->cwc_cycletime > 0 ? cw->cwc_cycletime : 0;
270 uint8_t cwc_ncwc = cw->cwc_next_cw_cycle < 2 ? cw->cwc_next_cw_cycle : 2;
271 if(checkcwcycle(cl, er, NULL, cw->cw, 0, cwc_ct, cwc_ncwc) != 0)
273 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);
275 else
277 #ifdef CS_CACHEEX_AIO
278 if(!er->localgenerated)
280 #endif
281 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);
282 cw->got_bad_cwc = 1; // no need to check it again
283 return 0;
284 #ifdef CS_CACHEEX_AIO
286 else
288 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);
290 #endif
292 #endif
293 return 1;
297 * This function returns cw (mostly received) in cache for er, or NULL if not found.
298 * IMPORTANT:
299 * - If found, DON'T forget to free returned ecm, because it is a copy useful to get data
300 * - If found, and cacheex_src client of returned ecm is not NULL, and we want to access it,
301 * remember to check for its validity (client structure is still existent)
302 * E.g.: if(ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill)
303 * We don't want make this stuff here to avoid useless cpu time if outside function we would not access to it.
305 struct ecm_request_t *check_cache(ECM_REQUEST *er, struct s_client *cl)
307 if(!cache_init_done || !er->csp_hash) return NULL;
309 ECM_REQUEST *ecm = NULL;
310 ECMHASH *result;
311 CW *cw;
312 uint64_t grp = cl?cl->grp:0;
314 SAFE_RWLOCK_RDLOCK(&cache_lock);
316 result = find_hash_table(&ht_cache, &er->csp_hash, sizeof(uint32_t),&compare_csp_hash);
317 cw = get_first_cw(result, er);
318 if (!cw)
319 goto out_err;
321 if(cw->csp // csp have no grp!
322 || !grp // csp client(no grp) searching for cache
323 || (grp && cw->grp // ecm group --> only when readers/ex-clients answer (e_found) it
324 && (grp & cw->grp)))
326 #ifdef CS_CACHEEX
327 //if preferlocalcards=2 for this ecm request, we can server ONLY cw from localcards readers until stage<3
328 if(er->preferlocalcards==2 && !cw->localcards && er->stage<3){
329 goto out_err;
332 CWCHECK check_cw = get_cwcheck(er);
334 if((!cw->proxy && !cw->localcards) // cw received from ONLY cacheex/csp peers
335 && check_cw.counter>1
336 && cw->count < check_cw.counter
337 && (check_cw.mode == 1 || !er->cacheex_wait_time_expired))
339 goto out_err;
342 #ifdef CS_CACHEEX_AIO
343 // client
344 if( cl && !cw->localgenerated
345 && !(chk_srvid_localgenerated_only_exception(er)) // service-based exception
346 && (cl->account->cacheex.localgenerated_only
347 || (chk_lg_only(er, &cl->account->cacheex.lg_only_tab))
348 ) // only lg-flagged CWs
351 goto out_err;
353 #endif
354 #endif
356 if (!cwcycle_check_cache(cl, er, cw))
357 goto out_err;
359 if (cs_malloc(&ecm, sizeof(ECM_REQUEST)))
361 ecm->rc = E_FOUND;
362 ecm->rcEx = 0;
363 memcpy(ecm->cw, cw->cw, 16);
364 ecm->grp = cw->grp;
365 ecm->selected_reader = cw->selected_reader;
366 ecm->cwc_cycletime = cw->cwc_cycletime;
367 ecm->cwc_next_cw_cycle = cw->cwc_next_cw_cycle;
368 ecm->cacheex_src = cw->cacheex_src;
369 #ifdef CS_CACHEEX_AIO
370 ecm->localgenerated = (cw->localgenerated) ? 1:0;
371 #endif
372 ecm->cw_count = cw->count;
376 out_err:
377 SAFE_RWLOCK_UNLOCK(&cache_lock);
378 return ecm;
381 #ifdef CS_CACHEEX_AIO
382 uint16_t get_cacheex_nopushafter(ECM_REQUEST *er)
384 return caidvaluetab_get_value(&cfg.cacheex_nopushafter_tab, er->caid, 0);
386 #endif
388 static void cacheex_cache_add(ECM_REQUEST *er, ECMHASH *result, CW *cw, bool add_new_cw)
390 (void)er; (void)result; (void)cw; (void)add_new_cw;
391 #ifdef CS_CACHEEX
392 er->cw_cache = cw;
393 cacheex_cache_push(er);
395 // cacheex debug log lines and cw diff stuff
396 if(!check_client(er->cacheex_src))
397 return;
399 #ifdef CS_CACHEEX_AIO
400 if (D_CACHEEX & cs_dblevel)
402 uint8_t remotenodeid[8];
403 cacheex_get_srcnodeid(er, remotenodeid);
405 if(!add_new_cw)
407 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));
408 return;
411 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));
413 else
415 #endif
416 if(!add_new_cw)
418 #ifdef CS_CACHEEX_AIO
419 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)" : "");
420 #else
421 debug_ecm(D_CACHEEX| D_CSP, "got duplicate pushed ECM %s from %s", buf, er->from_csp ? "csp" : username(er->cacheex_src));
422 #endif
423 return;
426 #ifdef CS_CACHEEX_AIO
427 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)" : "");
428 #else
429 debug_ecm(D_CACHEEX|D_CSP, "got pushed ECM %s from %s", buf, er->from_csp ? "csp" : username(er->cacheex_src));
430 #endif
432 #ifdef CS_CACHEEX_AIO
434 #endif
436 CW *cw_first = get_first_cw(result, er);
437 if(!cw_first)
438 return;
440 // compare er cw with mostly counted cached cw
441 if(memcmp(er->cw, cw_first->cw, sizeof(er->cw)) != 0)
443 er->cacheex_src->cwcacheexerrcw++;
444 if (er->cacheex_src->account)
445 er->cacheex_src->account->cwcacheexerrcw++;
447 if (((0x0200| 0x0800) & cs_dblevel)) // avoid useless operations if debug is not enabled
449 char cw1[16*3+2], cw2[16*3+2];
450 cs_hexdump(0, er->cw, 16, cw1, sizeof(cw1));
451 cs_hexdump(0, cw_first->cw, 16, cw2, sizeof(cw2));
453 char ip1[20]="", ip2[20]="";
454 if (check_client(er->cacheex_src))
455 cs_strncpy(ip1, cs_inet_ntoa(er->cacheex_src->ip), sizeof(ip1));
456 if (check_client(cw_first->cacheex_src))
457 cs_strncpy(ip2, cs_inet_ntoa(cw_first->cacheex_src->ip), sizeof(ip2));
458 else if (cw_first->selected_reader && check_client(cw_first->selected_reader->client))
459 cs_strncpy(ip2, cs_inet_ntoa(cw_first->selected_reader->client->ip), sizeof(ip2));
461 #ifdef CS_CACHEEX_AIO
462 uint8_t remotenodeid[8];
463 cacheex_get_srcnodeid(er, remotenodeid);
465 uint8_t fakeF0 = 0, offset = 0;
467 if(get_odd_even(er) == 0x81)
468 offset = 8;
471 (cw_first->cw[7+offset] != 0x00 && er->cw[7+offset] != 0x00)
472 && (cw_first->cw[7+offset] ^ 0xF0) == er->cw[7+offset]
475 fakeF0 = 1;
478 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,
479 #else
480 debug_ecm(D_CACHEEX| D_CSP, "WARNING: Different CWs %s from %s(%s)<>%s(%s): %s<>%s ", buf,
481 #endif
482 er->from_csp ? "csp" : username(er->cacheex_src), ip1,
483 check_client(cw_first->cacheex_src)?username(cw_first->cacheex_src):(cw_first->selected_reader?cw_first->selected_reader->label:"unknown/csp"), ip2,
484 #ifdef CS_CACHEEX_AIO
485 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]" : "");
486 #else
487 cw1, cw2);
488 #endif
489 #ifdef WITH_DEBUG
490 if(cs_dblevel & D_CACHEEX)
492 LL_LOCKITER *li = ll_li_create(er->csp_lastnodes, 0);
493 uint8_t *nodeid;
494 uint8_t hops = 0;
495 while((nodeid = ll_li_next(li)))
497 cs_log_dbg(D_CACHEEX, "Different CW-nodelist hop%02u: %" PRIu64 "X", ++hops, cacheex_node_id(nodeid));
499 ll_li_destroy(li);
501 #endif
504 #endif
507 #ifdef CS_CACHEEX_AIO
508 CW_CACHE_SETTING get_cw_cache(ECM_REQUEST *er)
510 int32_t i, timediff_old_cw = 0;
511 int8_t mode = 0;
513 for(i = 0; i < cfg.cw_cache_settings.cwchecknum; i++)
515 CWCHECKTAB_DATA *d = &cfg.cw_cache_settings.cwcheckdata[i];
517 if(i == 0 && d->caid <= 0)
519 mode = d->mode;
520 timediff_old_cw = d->counter;
521 continue; //check other, only valid for unset
524 if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1))
526 if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1)
528 if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1)
530 mode = d->mode;
531 timediff_old_cw = d->counter;
532 break;
538 //check for correct values
539 if(mode>3 || mode<0) mode=0;
540 if(timediff_old_cw<1) timediff_old_cw=0;
542 CW_CACHE_SETTING cw_cache_setting;
543 memset(&cw_cache_setting, 0, sizeof(CW_CACHE_SETTING));
544 cw_cache_setting.mode = mode;
545 cw_cache_setting.timediff_old_cw = timediff_old_cw;
547 return cw_cache_setting;
550 static bool cw_cache_check(ECM_REQUEST *er)
552 if(cw_cache_init_done)
554 CW_CACHE_SETTING cw_cache_setting = get_cw_cache(er);
555 if(cw_cache_setting.mode > 0)
557 CW_CACHE *cw_cache = NULL;
558 SAFE_RWLOCK_WRLOCK(&cw_cache_lock);
559 cw_cache = find_hash_table(&ht_cw_cache, &er->cw, sizeof(er->cw), &compare_cw_cache);
560 // add cw to ht_cw_cache if < cw_cache_size
561 if(!cw_cache)
563 // cw_cache-size(count/memory) pre-check
565 (cfg.cw_cache_size && (cfg.cw_cache_size > tommy_hashlin_count(&ht_cw_cache)))
566 || (cfg.cw_cache_memory && (cfg.cw_cache_memory*1024*1024 > (2 * tommy_hashlin_memory_usage(&ht_cw_cache))))
569 if(cs_malloc(&cw_cache, sizeof(CW_CACHE)))
571 memcpy(cw_cache->cw, er->cw, sizeof(er->cw));
572 cw_cache->caid = er->caid;
573 cw_cache->prid = er->prid;
574 cw_cache->srvid = er->srvid;
575 cs_ftime(&cw_cache->first_recv_time);
576 cs_ftime(&cw_cache->upd_time);
578 tommy_hashlin_insert(&ht_cw_cache, &cw_cache->ht_node, cw_cache, tommy_hash_u32(0, &er->cw, sizeof(er->cw)));
579 tommy_list_insert_tail(&ll_cw_cache, &cw_cache->ll_node, cw_cache);
581 SAFE_RWLOCK_UNLOCK(&cw_cache_lock);
582 return true;
584 else
586 SAFE_RWLOCK_UNLOCK(&cw_cache_lock);
587 cs_log("[cw_cache] ERROR: NO added HASH to cw_cache!!");
588 return false;
591 else
593 // clean cache call;
594 SAFE_RWLOCK_UNLOCK(&cw_cache_lock);
595 cw_cache_cleanup(false);
596 return false;
599 // cw found
600 else
602 char cw1[16*3+2];
603 char cw2[16*3+2];
604 int8_t drop_cw = 0;
605 int64_t gone_diff = 0;
607 gone_diff = comp_timeb(&er->tps, &cw_cache->first_recv_time);
609 if(D_CW_CACHE & cs_dblevel)
611 cs_hexdump(0, cw_cache->cw, 16, cw1, sizeof(cw1));
612 cs_hexdump(0, er->cw, 16, cw2, sizeof(cw2));
615 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
617 // log every dupe cw
618 if(cs_dblevel & D_CW_CACHE)
620 uint8_t remotenodeid[8];
621 cacheex_get_srcnodeid(er, remotenodeid);
622 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);
625 if(cw_cache->srvid == er->srvid && cw_cache->caid == er->caid) // same cw for same caid&srvid
627 cs_ftime(&cw_cache->upd_time);
628 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)" : "");
629 drop_cw=1;
632 else if(cw_cache->srvid != er->srvid) // same cw for different srvid & late
634 cs_ftime(&cw_cache->upd_time);
635 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)" : "");
636 drop_cw = 1;
638 else if(gone_diff > 15000) // same cw later as 15 secs
640 #ifdef WITH_DEBUG
641 if(cs_dblevel & D_CW_CACHE)
643 uint8_t remotenodeid[8];
644 cacheex_get_srcnodeid(er, remotenodeid);
645 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);
647 #endif
648 drop_cw = 1;
651 if(cw_cache_setting.mode > 1 && drop_cw)
653 // cw_cache->drop_count++;
654 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);
655 SAFE_RWLOCK_UNLOCK(&cw_cache_lock);
656 return false;
661 SAFE_RWLOCK_UNLOCK(&cw_cache_lock);
662 return true;
665 else
667 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);
668 return true;
670 return true;
672 #endif
674 void add_cache(ECM_REQUEST *er)
676 if(!cache_init_done || !er->csp_hash) return;
677 #ifdef CS_CACHEEX_AIO
678 // cw_cache_check
679 if(!cw_cache_check(er))
681 return;
683 #endif
684 ECMHASH *result = NULL;
685 CW *cw = NULL;
686 bool add_new_cw=false;
688 SAFE_RWLOCK_WRLOCK(&cache_lock);
690 // add csp_hash to cache
691 result = find_hash_table(&ht_cache, &er->csp_hash, sizeof(uint32_t), &compare_csp_hash);
692 if(!result)
694 if(cs_malloc(&result, sizeof(ECMHASH)))
696 result->csp_hash = er->csp_hash;
697 init_hash_table(&result->ht_cw, &result->ll_cw);
698 cs_ftime(&result->first_recv_time);
699 add_hash_table(&ht_cache, &result->ht_node, &ll_cache, &result->ll_node, result, &result->csp_hash, sizeof(uint32_t));
701 else
703 SAFE_RWLOCK_UNLOCK(&cache_lock);
704 cs_log("ERROR: NO added HASH to cache!!");
705 return;
709 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!
711 //add cw to this csp hash
712 cw = find_hash_table(&result->ht_cw, er->cw, sizeof(er->cw), &compare_cw);
714 if(!cw)
716 if(count_hash_table(&result->ht_cw) >= 10) // max 10 different cws stored
718 SAFE_RWLOCK_UNLOCK(&cache_lock);
719 return;
722 while(1)
724 if(cs_malloc(&cw, sizeof(CW)))
726 memcpy(cw->cw, er->cw, sizeof(er->cw));
727 cw->odd_even = get_odd_even(er);
728 cw->cwc_cycletime = er->cwc_cycletime;
729 cw->cwc_next_cw_cycle = er->cwc_next_cw_cycle;
730 cw->count= 0;
731 cw->csp = 0;
732 cw->cacheex = 0;
733 cw->localcards=0;
734 cw->proxy=0;
735 cw->grp = 0;
736 cw->caid = er->caid;
737 cw->prid = er->prid;
738 cw->srvid = er->srvid;
739 cw->selected_reader=er->selected_reader;
740 cw->cacheex_src=er->cacheex_src;
741 cw->pushout_client = NULL;
743 while(1)
745 if (pthread_rwlock_init(&cw->pushout_client_lock, NULL) == 0)
746 break;
748 cs_log("Error creating lock pushout_client_lock!");
749 cs_sleepms(1);
752 add_hash_table(&result->ht_cw, &cw->ht_node, &result->ll_cw, &cw->ll_node, cw, cw->cw, sizeof(er->cw));
753 add_new_cw=true;
754 break;
757 cs_log("ERROR: NO added CW to cache!! Re-trying...");
758 cs_sleepms(1);
762 // update if answered from csp/cacheex/local_proxy
763 if(er->from_cacheex) cw->cacheex = 1;
764 if(er->from_csp) cw->csp = 1;
765 if(!er->cacheex_src)
767 if(is_localreader(er->selected_reader, er)) cw->localcards=1;
768 else cw->proxy = 1;
771 #ifdef CS_CACHEEX_AIO
772 // copy flag for local generated CW
773 if(er->localgenerated || (er->selected_reader && !is_network_reader(er->selected_reader)))
775 cw->localgenerated = 1;
776 er->localgenerated = 1;
777 // to favorite CWs with this flag while sorting
778 if(cw->count < 0x0F000000)
780 cw->count |= 0x0F000000;
781 lg_cache_size++;
784 else
786 cw->localgenerated = 0;
788 #endif
790 // always update group and counter
791 cw->grp |= er->grp;
792 cw->count++;
794 #ifdef CS_CACHEEX_AIO
795 // add count to er for checking @ cacheex_push
796 er->cw_count += cw->count;
797 #endif
798 // sort cw_list by counter (DESC order)
799 if(cw->count>1)
800 sort_list(&result->ll_cw, count_sort);
802 #ifdef CS_CACHEEX_AIO
803 // dont push not flagged CWs - global
804 if(!er->localgenerated &&
806 !chk_srvid_localgenerated_only_exception(er)
807 && (cfg.cacheex_localgenerated_only || chk_lg_only(er, &cfg.cacheex_lg_only_tab))
810 cs_log_dbg(D_CACHEEX, "cacheex: push denied, cacheex_localgenerated_only->global");
811 SAFE_RWLOCK_UNLOCK(&cache_lock);
812 return;
815 // dont push CW if time for caid > x && from local reader | proxy
816 if(er->rc < 3 && er->ecm_time && get_cacheex_nopushafter(er) != 0 &&(get_cacheex_nopushafter(er) < er->ecm_time ))
818 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);
819 SAFE_RWLOCK_UNLOCK(&cache_lock);
820 return;
823 // no cacheex-push on diff-cw's if no localgenerated flag exist
824 if(cfg.cacheex_dropdiffs && (count_hash_table(&result->ht_cw) > 1) && !er->localgenerated)
826 cs_log_dbg(D_CACHEEX,"cacheex: diff CW - cacheex push denied src: %s", er->selected_reader->label);
827 SAFE_RWLOCK_UNLOCK(&cache_lock);
828 return;
830 #endif
832 SAFE_RWLOCK_UNLOCK(&cache_lock);
834 cacheex_cache_add(er, result, cw, add_new_cw);
837 #ifdef CS_CACHEEX_AIO
838 void cw_cache_cleanup(bool force)
840 if(!cw_cache_init_done)
841 { return; }
843 SAFE_RWLOCK_WRLOCK(&cw_cache_lock);
845 CW_CACHE *cw_cache;
846 node *i, *i_next;
848 uint32_t ll_c = 0;
849 uint32_t ll_ten_percent = (uint)tommy_list_count(&ll_cw_cache)*0.1; // 10 percent of cache
851 if(!force)
852 sort_list(&ll_cw_cache, time_sort);
854 i = get_first_node_list(&ll_cw_cache);
855 while(i)
857 i_next = i->next;
859 cw_cache = get_data_from_node(i);
861 if(!cw_cache)
863 i = i_next;
864 continue;
866 if(!force)
868 ++ll_c;
870 if(ll_c < ll_ten_percent)
872 remove_elem_list(&ll_cw_cache, &cw_cache->ll_node);
873 remove_elem_hash_table(&ht_cw_cache, &cw_cache->ht_node);
874 NULLFREE(cw_cache);
876 else{
877 break;
880 else
882 remove_elem_list(&ll_cw_cache, &cw_cache->ll_node);
883 remove_elem_hash_table(&ht_cw_cache, &cw_cache->ht_node);
884 NULLFREE(cw_cache);
887 i = i_next;
890 SAFE_RWLOCK_UNLOCK(&cw_cache_lock);
892 #endif
894 void cleanup_cache(bool force)
896 ECMHASH *ecmhash;
897 CW *cw;
898 struct s_pushclient *pc, *nxt;
899 node *i,*i_next,*j,*j_next;
901 struct timeb now;
902 int64_t gone_first, gone_upd;
904 if(!cache_init_done)
905 { return; }
907 SAFE_RWLOCK_WRLOCK(&cache_lock);
909 i = get_first_node_list(&ll_cache);
910 while(i)
912 i_next = i->next;
913 ecmhash = get_data_from_node(i);
915 if(!ecmhash)
917 i = i_next;
918 continue;
921 cs_ftime(&now);
922 gone_first = comp_timeb(&now, &ecmhash->first_recv_time);
923 gone_upd = comp_timeb(&now, &ecmhash->upd_time);
925 if(!force && gone_first<=(cfg.max_cache_time*1000)) // not continue, useless check for nexts one!
927 break;
930 if(force || gone_upd>(cfg.max_cache_time*1000))
932 j = get_first_node_list(&ecmhash->ll_cw);
933 while(j)
935 j_next = j->next;
936 cw = get_data_from_node(j);
937 if(cw)
939 pthread_rwlock_destroy(&cw->pushout_client_lock);
940 pc = cw->pushout_client;
941 cw->pushout_client=NULL;
942 while(pc)
944 nxt = pc->next_push;
945 NULLFREE(pc);
946 pc = nxt;
948 #ifdef CS_CACHEEX_AIO
949 if(cw->count >= 0x0F000000)
951 lg_cache_size--;
953 #endif
954 remove_elem_list(&ecmhash->ll_cw, &cw->ll_node);
955 remove_elem_hash_table(&ecmhash->ht_cw, &cw->ht_node);
956 NULLFREE(cw);
958 j = j_next;
961 deinitialize_hash_table(&ecmhash->ht_cw);
962 remove_elem_list(&ll_cache, &ecmhash->ll_node);
963 remove_elem_hash_table(&ht_cache, &ecmhash->ht_node);
964 NULLFREE(ecmhash);
966 i = i_next;
968 SAFE_RWLOCK_UNLOCK(&cache_lock);
971 #ifdef CS_CACHEEX_AIO
972 void cacheex_get_srcnodeid(ECM_REQUEST *er, uint8_t *remotenodeid)
974 uint8_t *data;
975 data = ll_last_element(er->csp_lastnodes);
976 if(data)
978 memcpy(remotenodeid, data, 8);
980 else
982 memset(remotenodeid, 0 , 8);
985 #endif