void cacheex_timeout(ECM_REQUEST *er) { if(er->cacheex_wait_time_expired) return; er->cacheex_wait_time_expired = 1; if(er->rc >= E_UNHANDLED) { cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} cacheex timeout! ", (check_client(er->client) ? er->client->account->usr : "******"), er->caid, er->prid, er->srvid); // if check_cw mode=0, first try to get cw from cache without check counter! CWCHECK check_cw = get_cwcheck(er); if(!check_cw.mode) { struct ecm_request_t *ecm = NULL; ecm = check_cache(er, er->client); if(ecm) // found in cache { struct s_write_from_cache *wfc = NULL; if(!cs_malloc(&wfc, sizeof(struct s_write_from_cache))) { NULLFREE(ecm); return; } wfc->er_new = er; wfc->er_cache = ecm; if(!add_job(er->client, ACTION_ECM_ANSWER_CACHE, wfc, sizeof(struct s_write_from_cache))) // write_ecm_answer_fromcache { NULLFREE(ecm); } return; } } // check if "normal" readers selected, if not send NOT FOUND! // cacheex1-client (having always no "normal" reader), // or not-cacheex-1 client with no normal readers available (or filtered by LB) if((er->reader_count + er->fallback_reader_count - er->cacheex_reader_count) <= 0) { if(!cfg.wait_until_ctimeout) { er->rc = E_NOTFOUND; er->selected_reader = NULL; er->rcEx = 0; cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} cacheex timeout: NO \"normal\" readers... not_found! ", (check_client(er->client) ? er->client->account->usr : "******"), er->caid, er->prid, er->srvid); send_dcw(er->client, er); return; } } else { if(er->stage < 2) { debug_ecm(D_TRACE, "request for %s %s", username(er->client), buf); request_cw_from_readers(er, 0); } } } }
void cacheex_push_out(struct s_client *cl, ECM_REQUEST *er) { int32_t res = 0, stats = -1; struct s_reader *reader = cl->reader; struct s_module *module = get_module(cl); // cc-nodeid-list-check if(reader) { if(reader->ph.c_cache_push_chk && !reader->ph.c_cache_push_chk(cl, er)) return; res = reader->ph.c_cache_push(cl, er); stats = cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 0); } else { if(module->c_cache_push_chk && !module->c_cache_push_chk(cl, er)) return; res = module->c_cache_push(cl, er); } debug_ecm(D_CACHEEX, "pushed ECM %s to %s res %d stats %d", buf, username(cl), res, stats); cl->cwcacheexpush++; if(cl->account) { cl->account->cwcacheexpush++; } first_client->cwcacheexpush++; }
void *work_thread(void *ptr) { struct job_data *data = (struct job_data *)ptr; struct s_client *cl = data->cl; struct s_reader *reader = cl->reader; struct timeb start, end; // start time poll, end time poll struct job_data tmp_data; struct pollfd pfd[1]; pthread_setspecific(getclient, cl); cl->thread = pthread_self(); cl->thread_active = 1; set_work_thread_name(data); struct s_module *module = get_module(cl); uint16_t bufsize = module->bufsize; //CCCam needs more than 1024bytes! if(!bufsize) { bufsize = 1024; } uint8_t *mbuf; if(!cs_malloc(&mbuf, bufsize)) { return NULL; } cl->work_mbuf = mbuf; // Track locally allocated data, because some callback may call cs_exit/cs_disconect_client/pthread_exit and then mbuf would be leaked int32_t n = 0, rc = 0, i, idx, s; uint8_t dcw[16]; int8_t restart_reader = 0; while(cl->thread_active) { cs_ftime(&start); // register start time while(cl->thread_active) { if(!cl || cl->kill || !is_valid_client(cl)) { pthread_mutex_lock(&cl->thread_lock); cl->thread_active = 0; pthread_mutex_unlock(&cl->thread_lock); cs_debug_mask(D_TRACE, "ending thread (kill)"); __free_job_data(cl, data); cl->work_mbuf = NULL; // Prevent free_client from freeing mbuf (->work_mbuf) free_client(cl); if(restart_reader) { restart_cardreader(reader, 0); } NULLFREE(mbuf); pthread_exit(NULL); return NULL; } if(data && data->action != ACTION_READER_CHECK_HEALTH) { cs_debug_mask(D_TRACE, "data from add_job action=%d client %c %s", data->action, cl->typ, username(cl)); } if(!data) { if(!cl->kill && cl->typ != 'r') { client_check_status(cl); } // do not call for physical readers as this might cause an endless job loop pthread_mutex_lock(&cl->thread_lock); if(cl->joblist && ll_count(cl->joblist) > 0) { LL_ITER itr = ll_iter_create(cl->joblist); data = ll_iter_next_remove(&itr); if(data) { set_work_thread_name(data); } //cs_debug_mask(D_TRACE, "start next job from list action=%d", data->action); } pthread_mutex_unlock(&cl->thread_lock); } if(!data) { /* for serial client cl->pfd is file descriptor for serial port not socket for example: pfd=open("/dev/ttyUSB0"); */ if(!cl->pfd || module->listenertype == LIS_SERIAL) { break; } pfd[0].fd = cl->pfd; pfd[0].events = POLLIN | POLLPRI; pthread_mutex_lock(&cl->thread_lock); cl->thread_active = 2; pthread_mutex_unlock(&cl->thread_lock); rc = poll(pfd, 1, 3000); pthread_mutex_lock(&cl->thread_lock); cl->thread_active = 1; pthread_mutex_unlock(&cl->thread_lock); if(rc > 0) { cs_ftime(&end); // register end time cs_debug_mask(D_TRACE, "[OSCAM-WORK] new event %d occurred on fd %d after %"PRId64" ms inactivity", pfd[0].revents, pfd[0].fd, comp_timeb(&end, &start)); data = &tmp_data; data->ptr = NULL; cs_ftime(&start); // register start time for new poll next run if(reader) { data->action = ACTION_READER_REMOTE; } else { if(cl->is_udp) { data->action = ACTION_CLIENT_UDP; data->ptr = mbuf; data->len = bufsize; } else { data->action = ACTION_CLIENT_TCP; } if(pfd[0].revents & (POLLHUP | POLLNVAL | POLLERR)) { cl->kill = 1; } } } } if(!data) { continue; } if(!reader && data->action < ACTION_CLIENT_FIRST) { __free_job_data(cl, data); break; } if(!data->action) { break; } struct timeb actualtime; cs_ftime(&actualtime); int32_t gone = comp_timeb(&actualtime, &data->time); if(data != &tmp_data && gone > (int) cfg.ctimeout+1000) { cs_debug_mask(D_TRACE, "dropping client data for %s time %dms", username(cl), gone); __free_job_data(cl, data); continue; } if(data != &tmp_data) { cl->work_job_data = data; } // Track the current job_data switch(data->action) { case ACTION_READER_IDLE: reader_do_idle(reader); break; case ACTION_READER_REMOTE: s = check_fd_for_data(cl->pfd); if(s == 0) // no data, another thread already read from fd? { break; } if(s < 0) { if(reader->ph.type == MOD_CONN_TCP) { network_tcp_connection_close(reader, "disconnect"); } break; } rc = reader->ph.recv(cl, mbuf, bufsize); if(rc < 0) { if(reader->ph.type == MOD_CONN_TCP) { network_tcp_connection_close(reader, "disconnect on receive"); } break; } cl->last = time(NULL); // *********************************** TO BE REPLACE BY CS_FTIME() LATER **************** idx = reader->ph.c_recv_chk(cl, dcw, &rc, mbuf, rc); if(idx < 0) { break; } // no dcw received if(!idx) { idx = cl->last_idx; } reader->last_g = time(NULL); // *********************************** TO BE REPLACE BY CS_FTIME() LATER **************** // for reconnect timeout for(i = 0, n = 0; i < cfg.max_pending && n == 0; i++) { if(cl->ecmtask[i].idx == idx) { cl->pending--; casc_check_dcw(reader, i, rc, dcw); n++; } } break; case ACTION_READER_RESET: cardreader_do_reset(reader); break; case ACTION_READER_ECM_REQUEST: reader_get_ecm(reader, data->ptr); break; case ACTION_READER_EMM: reader_do_emm(reader, data->ptr); break; case ACTION_READER_CARDINFO: reader_do_card_info(reader); break; case ACTION_READER_INIT: if(!cl->init_done) { reader_init(reader); } break; case ACTION_READER_RESTART: cl->kill = 1; restart_reader = 1; break; case ACTION_READER_RESET_FAST: reader->card_status = CARD_NEED_INIT; cardreader_do_reset(reader); break; case ACTION_READER_CHECK_HEALTH: cardreader_do_checkhealth(reader); break; case ACTION_READER_CAPMT_NOTIFY: if(reader->ph.c_capmt) { reader->ph.c_capmt(cl, data->ptr); } break; case ACTION_CLIENT_UDP: n = module->recv(cl, data->ptr, data->len); if(n < 0) { break; } module->s_handler(cl, data->ptr, n); break; case ACTION_CLIENT_TCP: s = check_fd_for_data(cl->pfd); if(s == 0) // no data, another thread already read from fd? { break; } if(s < 0) // system error or fd wants to be closed { cl->kill = 1; // kill client on next run continue; } n = module->recv(cl, mbuf, bufsize); if(n < 0) { cl->kill = 1; // kill client on next run continue; } module->s_handler(cl, mbuf, n); break; case ACTION_CACHEEX_TIMEOUT: #ifdef CS_CACHEEX cacheex_timeout(data->ptr); #endif break; case ACTION_FALLBACK_TIMEOUT: fallback_timeout(data->ptr); break; case ACTION_CLIENT_TIMEOUT: ecm_timeout(data->ptr); break; case ACTION_ECM_ANSWER_READER: chk_dcw(data->ptr); break; case ACTION_ECM_ANSWER_CACHE: write_ecm_answer_fromcache(data->ptr); break; case ACTION_CLIENT_INIT: if(module->s_init) { module->s_init(cl); } cl->is_udp = module->type == MOD_CONN_UDP; cl->init_done = 1; break; case ACTION_CLIENT_IDLE: if(module->s_idle) { module->s_idle(cl); } else { cs_log("user %s reached %d sec idle limit.", username(cl), cfg.cmaxidle); cl->kill = 1; } break; case ACTION_CACHE_PUSH_OUT: { #ifdef CS_CACHEEX ECM_REQUEST *er = data->ptr; int32_t res = 0, stats = -1; // cc-nodeid-list-check if(reader) { if(reader->ph.c_cache_push_chk && !reader->ph.c_cache_push_chk(cl, er)) { break; } res = reader->ph.c_cache_push(cl, er); stats = cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 0); } else { if(module->c_cache_push_chk && !module->c_cache_push_chk(cl, er)) { break; } res = module->c_cache_push(cl, er); } debug_ecm(D_CACHEEX, "pushed ECM %s to %s res %d stats %d", buf, username(cl), res, stats); cl->cwcacheexpush++; if(cl->account) { cl->account->cwcacheexpush++; } first_client->cwcacheexpush++; #endif break; } case ACTION_CLIENT_KILL: cl->kill = 1; break; case ACTION_CLIENT_SEND_MSG: { #ifdef MODULE_CCCAM struct s_clientmsg *clientmsg = (struct s_clientmsg *)data->ptr; cc_cmd_send(cl, clientmsg->msg, clientmsg->len, clientmsg->cmd); #endif break; } } // switch __free_job_data(cl, data); } if(thread_pipe[1] && (mbuf[0] != 0x00)) { cs_ddump_mask(D_TRACE, mbuf, 1, "[OSCAM-WORK] Write to pipe:"); if(write(thread_pipe[1], mbuf, 1) == -1) // wakeup client check { cs_debug_mask(D_TRACE, "[OSCAM-WORK] Writing to pipe failed (errno=%d %s)", errno, strerror(errno)); } } // Check for some race condition where while we ended, another thread added a job pthread_mutex_lock(&cl->thread_lock); if(cl->joblist && ll_count(cl->joblist) > 0) { pthread_mutex_unlock(&cl->thread_lock); continue; } else { cl->thread_active = 0; pthread_mutex_unlock(&cl->thread_lock); break; } } cl->thread_active = 0; cl->work_mbuf = NULL; // Prevent free_client from freeing mbuf (->work_mbuf) NULLFREE(mbuf); pthread_exit(NULL); return NULL; }
void add_cache(ECM_REQUEST *er){ if(!er->csp_hash) return; ECMHASH *result = NULL; CW *cw = NULL; #ifdef CS_CACHEEX bool add_new_cw=false; #endif pthread_rwlock_wrlock(&cache_lock); //add csp_hash to cache result = find_hash_table(&ht_cache, &er->csp_hash, sizeof(int32_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(int32_t)); }else{ pthread_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 pthread_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; #ifdef CS_CACHEEX cw->cacheex_src=er->cacheex_src; #endif 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)); #ifdef CS_CACHEEX add_new_cw=true; #endif 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; #ifdef CS_CACHEEX if(!er->cacheex_src){ #endif if(is_localreader(er->selected_reader, er)) cw->localcards=1; else cw->proxy = 1; #ifdef CS_CACHEEX } #endif //always update group and counter cw->grp |= er->grp; cw->count++; //sort cw_list by counter (DESC order) if(cw->count>1) sort_list(&result->ll_cw, count_sort); pthread_rwlock_unlock(&cache_lock); #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)){ if(add_new_cw){ debug_ecm(D_CACHEEX|D_CSP, "got pushed ECM %s from %s", buf, er->from_csp ? "csp" : username(er->cacheex_src)); CW *cw_first = get_first_cw(result, er); if(er && cw_first){ //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)); debug_ecm(D_CACHEEX| D_CSP, "WARNING: Different CWs %s from %s(%s)<>%s(%s): %s<>%s ", buf, 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, cw1, cw2); } } } }else debug_ecm(D_CACHEEX| D_CSP, "got duplicate pushed ECM %s from %s", buf, er->from_csp ? "csp" : username(er->cacheex_src)); } #endif }