1 | #include "globals.h"
|
---|
2 | #include "module-cacheex.h"
|
---|
3 | #include "oscam-client.h"
|
---|
4 | #include "oscam-ecm.h"
|
---|
5 | #include "oscam-emm.h"
|
---|
6 | #include "oscam-lock.h"
|
---|
7 | #include "oscam-net.h"
|
---|
8 | #include "oscam-reader.h"
|
---|
9 | #include "oscam-string.h"
|
---|
10 | #include "oscam-work.h"
|
---|
11 | #include "reader-common.h"
|
---|
12 | #include "module-cccam-data.h"
|
---|
13 | #include "module-cccshare.h"
|
---|
14 |
|
---|
15 | extern CS_MUTEX_LOCK system_lock;
|
---|
16 | extern int32_t thread_pipe[2];
|
---|
17 |
|
---|
18 | struct job_data {
|
---|
19 | enum actions action;
|
---|
20 | struct s_reader *rdr;
|
---|
21 | struct s_client *cl;
|
---|
22 | void *ptr;
|
---|
23 | time_t time;
|
---|
24 | uint16_t len;
|
---|
25 | };
|
---|
26 |
|
---|
27 | static void free_job_data(struct job_data *data)
|
---|
28 | {
|
---|
29 | if (!data)
|
---|
30 | return;
|
---|
31 | if (data->len && data->ptr)
|
---|
32 | free(data->ptr);
|
---|
33 | free(data);
|
---|
34 | }
|
---|
35 |
|
---|
36 | void free_joblist(struct s_client *cl) {
|
---|
37 | pthread_mutex_trylock(&cl->thread_lock);
|
---|
38 | LL_ITER it = ll_iter_create(cl->joblist);
|
---|
39 | struct job_data *data;
|
---|
40 | while ((data = ll_iter_next(&it))) {
|
---|
41 | free_job_data(data);
|
---|
42 | }
|
---|
43 | ll_destroy(cl->joblist);
|
---|
44 | cl->joblist = NULL;
|
---|
45 | cl->account = NULL;
|
---|
46 | pthread_mutex_unlock(&cl->thread_lock);
|
---|
47 | pthread_mutex_destroy(&cl->thread_lock);
|
---|
48 | }
|
---|
49 |
|
---|
50 | /*
|
---|
51 | Work threads are named like this:
|
---|
52 | w[r|c]XX-[rdr->label|client->username]
|
---|
53 |
|
---|
54 | w - work thread prefix
|
---|
55 | [r|c] - depending whether the the action is related to reader or client
|
---|
56 | XX - two digit action code from enum actions
|
---|
57 | label - reader label or client username (see username() function)
|
---|
58 | */
|
---|
59 | static void set_work_thread_name(struct job_data *data) {
|
---|
60 | char thread_name[16 + 1];
|
---|
61 | snprintf(thread_name, sizeof(thread_name), "w%c%02d-%s",
|
---|
62 | data->action < ACTION_CLIENT_FIRST ? 'r' : 'c',
|
---|
63 | data->action,
|
---|
64 | username(data->cl)
|
---|
65 | );
|
---|
66 | set_thread_name(thread_name);
|
---|
67 | }
|
---|
68 |
|
---|
69 | void * work_thread(void *ptr) {
|
---|
70 | struct job_data *data = (struct job_data *)ptr;
|
---|
71 | struct s_client *cl = data->cl;
|
---|
72 | struct s_reader *reader = cl->reader;
|
---|
73 |
|
---|
74 | struct job_data tmp_data;
|
---|
75 | struct pollfd pfd[1];
|
---|
76 |
|
---|
77 | pthread_setspecific(getclient, cl);
|
---|
78 | cl->thread = pthread_self();
|
---|
79 | cl->thread_active = 1;
|
---|
80 |
|
---|
81 | set_work_thread_name(data);
|
---|
82 |
|
---|
83 | struct s_module *module = get_module(cl);
|
---|
84 | uint16_t bufsize = module->bufsize; //CCCam needs more than 1024bytes!
|
---|
85 | if (!bufsize)
|
---|
86 | bufsize = 1024;
|
---|
87 |
|
---|
88 | uint8_t *mbuf;
|
---|
89 | if (!cs_malloc(&mbuf, bufsize))
|
---|
90 | return NULL;
|
---|
91 | int32_t n = 0, rc = 0, i, idx, s;
|
---|
92 | uint8_t dcw[16];
|
---|
93 | time_t now;
|
---|
94 | int8_t restart_reader=0;
|
---|
95 | while (cl->thread_active) {
|
---|
96 | while (cl->thread_active) {
|
---|
97 | if (!cl || cl->kill || !is_valid_client(cl)) {
|
---|
98 | pthread_mutex_lock(&cl->thread_lock);
|
---|
99 | cl->thread_active = 0;
|
---|
100 | pthread_mutex_unlock(&cl->thread_lock);
|
---|
101 | cs_debug_mask(D_TRACE, "ending thread (kill)");
|
---|
102 | if (data && data != &tmp_data)
|
---|
103 | free_job_data(data);
|
---|
104 | data = NULL;
|
---|
105 | free_client(cl);
|
---|
106 | if (restart_reader)
|
---|
107 | restart_cardreader(reader, 0);
|
---|
108 | free(mbuf);
|
---|
109 | pthread_exit(NULL);
|
---|
110 | return NULL;
|
---|
111 | }
|
---|
112 |
|
---|
113 | if (data && data->action != ACTION_READER_CHECK_HEALTH)
|
---|
114 | cs_debug_mask(D_TRACE, "data from add_job action=%d client %c %s", data->action, cl->typ, username(cl));
|
---|
115 |
|
---|
116 | if (!data) {
|
---|
117 | if (!cl->kill && cl->typ != 'r')
|
---|
118 | client_check_status(cl); // do not call for physical readers as this might cause an endless job loop
|
---|
119 | pthread_mutex_lock(&cl->thread_lock);
|
---|
120 | if (cl->joblist && ll_count(cl->joblist)>0) {
|
---|
121 | LL_ITER itr = ll_iter_create(cl->joblist);
|
---|
122 | data = ll_iter_next_remove(&itr);
|
---|
123 | if (data)
|
---|
124 | set_work_thread_name(data);
|
---|
125 | //cs_debug_mask(D_TRACE, "start next job from list action=%d", data->action);
|
---|
126 | }
|
---|
127 | pthread_mutex_unlock(&cl->thread_lock);
|
---|
128 | }
|
---|
129 |
|
---|
130 | if (!data) {
|
---|
131 | /* for serial client cl->pfd is file descriptor for serial port not socket
|
---|
132 | for example: pfd=open("/dev/ttyUSB0"); */
|
---|
133 | if (!cl->pfd || module->listenertype == LIS_SERIAL)
|
---|
134 | break;
|
---|
135 | pfd[0].fd = cl->pfd;
|
---|
136 | pfd[0].events = POLLIN | POLLPRI | POLLHUP;
|
---|
137 |
|
---|
138 | pthread_mutex_lock(&cl->thread_lock);
|
---|
139 | cl->thread_active = 2;
|
---|
140 | pthread_mutex_unlock(&cl->thread_lock);
|
---|
141 | rc = poll(pfd, 1, 3000);
|
---|
142 | pthread_mutex_lock(&cl->thread_lock);
|
---|
143 | cl->thread_active = 1;
|
---|
144 | pthread_mutex_unlock(&cl->thread_lock);
|
---|
145 |
|
---|
146 | if (rc == -1)
|
---|
147 | cs_debug_mask(D_TRACE, "poll() timeout");
|
---|
148 |
|
---|
149 | if (rc > 0) {
|
---|
150 | data = &tmp_data;
|
---|
151 | data->ptr = NULL;
|
---|
152 |
|
---|
153 | if (reader)
|
---|
154 | data->action = ACTION_READER_REMOTE;
|
---|
155 | else {
|
---|
156 | if (cl->is_udp) {
|
---|
157 | data->action = ACTION_CLIENT_UDP;
|
---|
158 | data->ptr = mbuf;
|
---|
159 | data->len = bufsize;
|
---|
160 | }
|
---|
161 | else
|
---|
162 | data->action = ACTION_CLIENT_TCP;
|
---|
163 | if (pfd[0].revents & (POLLHUP | POLLNVAL))
|
---|
164 | cl->kill = 1;
|
---|
165 | }
|
---|
166 | }
|
---|
167 | }
|
---|
168 |
|
---|
169 | if (!data)
|
---|
170 | continue;
|
---|
171 |
|
---|
172 | if (!reader && data->action < ACTION_CLIENT_FIRST) {
|
---|
173 | if (data != &tmp_data)
|
---|
174 | free_job_data(data);
|
---|
175 | data = NULL;
|
---|
176 | break;
|
---|
177 | }
|
---|
178 |
|
---|
179 | if (!data->action)
|
---|
180 | break;
|
---|
181 |
|
---|
182 | now = time(NULL);
|
---|
183 | time_t diff = (time_t)(cfg.ctimeout/1000)+1;
|
---|
184 | if (data != &tmp_data && data->time < now-diff) {
|
---|
185 | cs_debug_mask(D_TRACE, "dropping client data for %s time %ds", username(cl), (int32_t)(now-data->time));
|
---|
186 | free_job_data(data);
|
---|
187 | data = NULL;
|
---|
188 | continue;
|
---|
189 | }
|
---|
190 |
|
---|
191 | switch (data->action) {
|
---|
192 | case ACTION_READER_IDLE:
|
---|
193 | reader_do_idle(reader);
|
---|
194 | break;
|
---|
195 | case ACTION_READER_REMOTE:
|
---|
196 | s = check_fd_for_data(cl->pfd);
|
---|
197 | if (s == 0) // no data, another thread already read from fd?
|
---|
198 | break;
|
---|
199 | if (s < 0) {
|
---|
200 | if (reader->ph.type==MOD_CONN_TCP)
|
---|
201 | network_tcp_connection_close(reader, "disconnect");
|
---|
202 | break;
|
---|
203 | }
|
---|
204 | rc = reader->ph.recv(cl, mbuf, bufsize);
|
---|
205 | if (rc < 0) {
|
---|
206 | if (reader->ph.type==MOD_CONN_TCP)
|
---|
207 | network_tcp_connection_close(reader, "disconnect on receive");
|
---|
208 | break;
|
---|
209 | }
|
---|
210 | cl->last = now;
|
---|
211 | idx = reader->ph.c_recv_chk(cl, dcw, &rc, mbuf, rc);
|
---|
212 | if (idx<0) break; // no dcw received
|
---|
213 | if (!idx) idx=cl->last_idx;
|
---|
214 | reader->last_g = now; // for reconnect timeout
|
---|
215 | for (i = 0, n = 0; i < cfg.max_pending && n == 0; i++) {
|
---|
216 | if (cl->ecmtask[i].idx==idx) {
|
---|
217 | cl->pending--;
|
---|
218 | casc_check_dcw(reader, i, rc, dcw);
|
---|
219 | n++;
|
---|
220 | }
|
---|
221 | }
|
---|
222 | break;
|
---|
223 | case ACTION_READER_RESET:
|
---|
224 | cardreader_do_reset(reader);
|
---|
225 | break;
|
---|
226 | case ACTION_READER_ECM_REQUEST:
|
---|
227 | reader_get_ecm(reader, data->ptr);
|
---|
228 | break;
|
---|
229 | case ACTION_READER_EMM:
|
---|
230 | reader_do_emm(reader, data->ptr);
|
---|
231 | break;
|
---|
232 | case ACTION_READER_CARDINFO:
|
---|
233 | reader_do_card_info(reader);
|
---|
234 | break;
|
---|
235 | case ACTION_READER_INIT:
|
---|
236 | if (!cl->init_done)
|
---|
237 | reader_init(reader);
|
---|
238 | break;
|
---|
239 | case ACTION_READER_RESTART:
|
---|
240 | cl->kill = 1;
|
---|
241 | restart_reader = 1;
|
---|
242 | break;
|
---|
243 | case ACTION_READER_RESET_FAST:
|
---|
244 | reader->card_status = CARD_NEED_INIT;
|
---|
245 | cardreader_do_reset(reader);
|
---|
246 | break;
|
---|
247 | case ACTION_READER_CHECK_HEALTH:
|
---|
248 | cardreader_do_checkhealth(reader);
|
---|
249 | break;
|
---|
250 | case ACTION_CLIENT_UDP:
|
---|
251 | n = module->recv(cl, data->ptr, data->len);
|
---|
252 | if (n < 0) break;
|
---|
253 | module->s_handler(cl, data->ptr, n);
|
---|
254 | break;
|
---|
255 | case ACTION_CLIENT_TCP:
|
---|
256 | s = check_fd_for_data(cl->pfd);
|
---|
257 | if (s == 0) // no data, another thread already read from fd?
|
---|
258 | break;
|
---|
259 | if (s < 0) { // system error or fd wants to be closed
|
---|
260 | cl->kill=1; // kill client on next run
|
---|
261 | continue;
|
---|
262 | }
|
---|
263 | n = module->recv(cl, mbuf, bufsize);
|
---|
264 | if (n < 0) {
|
---|
265 | cl->kill=1; // kill client on next run
|
---|
266 | continue;
|
---|
267 | }
|
---|
268 | module->s_handler(cl, mbuf, n);
|
---|
269 | break;
|
---|
270 | case ACTION_CLIENT_ECM_ANSWER:
|
---|
271 | chk_dcw(cl, data->ptr);
|
---|
272 | break;
|
---|
273 | case ACTION_CLIENT_INIT:
|
---|
274 | if (module->s_init)
|
---|
275 | module->s_init(cl);
|
---|
276 | cl->is_udp = module->type == MOD_CONN_UDP;
|
---|
277 | cl->init_done=1;
|
---|
278 | break;
|
---|
279 | case ACTION_CLIENT_IDLE:
|
---|
280 | if (module->s_idle)
|
---|
281 | module->s_idle(cl);
|
---|
282 | else {
|
---|
283 | cs_log("user %s reached %d sec idle limit.", username(cl), cfg.cmaxidle);
|
---|
284 | cl->kill = 1;
|
---|
285 | }
|
---|
286 | break;
|
---|
287 | case ACTION_CACHE_PUSH_OUT: {
|
---|
288 | #ifdef CS_CACHEEX
|
---|
289 | ECM_REQUEST *er = data->ptr;
|
---|
290 | int32_t res=0, stats = -1;
|
---|
291 | // cc-nodeid-list-check
|
---|
292 | if (reader) {
|
---|
293 | if (reader->ph.c_cache_push_chk && !reader->ph.c_cache_push_chk(cl, er))
|
---|
294 | break;
|
---|
295 | res = reader->ph.c_cache_push(cl, er);
|
---|
296 | stats = cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 0);
|
---|
297 | } else {
|
---|
298 | if (module->c_cache_push_chk && !module->c_cache_push_chk(cl, er))
|
---|
299 | break;
|
---|
300 | res = module->c_cache_push(cl, er);
|
---|
301 | }
|
---|
302 | debug_ecm(D_CACHEEX, "pushed ECM %s to %s res %d stats %d", buf, username(cl), res, stats);
|
---|
303 | cl->cwcacheexpush++;
|
---|
304 | if (cl->account)
|
---|
305 | cl->account->cwcacheexpush++;
|
---|
306 | first_client->cwcacheexpush++;
|
---|
307 | #endif
|
---|
308 | break;
|
---|
309 | }
|
---|
310 | case ACTION_CLIENT_KILL:
|
---|
311 | cl->kill = 1;
|
---|
312 | break;
|
---|
313 | case ACTION_CLIENT_SEND_MSG: {
|
---|
314 | #ifdef MODULE_CCCAM
|
---|
315 | struct s_clientmsg *clientmsg = (struct s_clientmsg *)data->ptr;
|
---|
316 | cc_cmd_send(cl, clientmsg->msg, clientmsg->len, clientmsg->cmd);
|
---|
317 | #endif
|
---|
318 | break;
|
---|
319 | }
|
---|
320 | } // switch
|
---|
321 |
|
---|
322 | if (data != &tmp_data)
|
---|
323 | free_job_data(data);
|
---|
324 | data = NULL;
|
---|
325 | }
|
---|
326 |
|
---|
327 | if (thread_pipe[1]) {
|
---|
328 | if (write(thread_pipe[1], mbuf, 1) == -1) { // wakeup client check
|
---|
329 | cs_debug_mask(D_TRACE, "Writing to pipe failed (errno=%d %s)", errno, strerror(errno));
|
---|
330 | }
|
---|
331 | }
|
---|
332 |
|
---|
333 | // Check for some race condition where while we ended, another thread added a job
|
---|
334 | pthread_mutex_lock(&cl->thread_lock);
|
---|
335 | if (cl->joblist && ll_count(cl->joblist) > 0) {
|
---|
336 | pthread_mutex_unlock(&cl->thread_lock);
|
---|
337 | continue;
|
---|
338 | } else {
|
---|
339 | cl->thread_active = 0;
|
---|
340 | pthread_mutex_unlock(&cl->thread_lock);
|
---|
341 | break;
|
---|
342 | }
|
---|
343 | }
|
---|
344 | free(mbuf);
|
---|
345 | pthread_exit(NULL);
|
---|
346 | cl->thread_active = 0;
|
---|
347 | return NULL;
|
---|
348 | }
|
---|
349 |
|
---|
350 | /**
|
---|
351 | * adds a job to the job queue
|
---|
352 | * if ptr should be free() after use, set len to the size
|
---|
353 | * else set size to 0
|
---|
354 | **/
|
---|
355 | int32_t add_job(struct s_client *cl, enum actions action, void *ptr, int32_t len)
|
---|
356 | {
|
---|
357 | if (!cl || cl->kill) {
|
---|
358 | if (!cl)
|
---|
359 | cs_log("WARNING: add_job failed."); // Ignore jobs for killed clients
|
---|
360 | if (len && ptr)
|
---|
361 | free(ptr);
|
---|
362 | return 0;
|
---|
363 | }
|
---|
364 |
|
---|
365 | #ifdef CS_CACHEEX
|
---|
366 | // Avoid full running queues:
|
---|
367 | if (action == ACTION_CACHE_PUSH_OUT && ll_count(cl->joblist) > 2000) {
|
---|
368 | cs_debug_mask(D_TRACE, "WARNING: job queue %s %s has more than 2000 jobs! count=%d, dropped!",
|
---|
369 | cl->typ=='c' ? "client" : "reader",
|
---|
370 | username(cl), ll_count(cl->joblist));
|
---|
371 | if (len && ptr)
|
---|
372 | free(ptr);
|
---|
373 | // Thread down???
|
---|
374 | pthread_mutex_lock(&cl->thread_lock);
|
---|
375 | if (cl->thread_active) {
|
---|
376 | // Just test for invalid thread id:
|
---|
377 | if (pthread_detach(cl->thread) == ESRCH) {
|
---|
378 | cl->thread_active = 0;
|
---|
379 | cs_debug_mask(D_TRACE, "WARNING: %s %s thread died!",
|
---|
380 | cl->typ == 'c' ? "client" : "reader", username(cl));
|
---|
381 | }
|
---|
382 | }
|
---|
383 | pthread_mutex_unlock(&cl->thread_lock);
|
---|
384 | return 0;
|
---|
385 | }
|
---|
386 | #endif
|
---|
387 |
|
---|
388 | struct job_data *data;
|
---|
389 | if (!cs_malloc(&data, sizeof(struct job_data))) {
|
---|
390 | if (len && ptr)
|
---|
391 | free(ptr);
|
---|
392 | return 0;
|
---|
393 | }
|
---|
394 |
|
---|
395 | data->action = action;
|
---|
396 | data->ptr = ptr;
|
---|
397 | data->cl = cl;
|
---|
398 | data->len = len;
|
---|
399 | data->time = time(NULL);
|
---|
400 |
|
---|
401 | pthread_mutex_lock(&cl->thread_lock);
|
---|
402 | if (cl->thread_active) {
|
---|
403 | if (!cl->joblist)
|
---|
404 | cl->joblist = ll_create("joblist");
|
---|
405 | ll_append(cl->joblist, data);
|
---|
406 | if (cl->thread_active == 2)
|
---|
407 | pthread_kill(cl->thread, OSCAM_SIGNAL_WAKEUP);
|
---|
408 | pthread_mutex_unlock(&cl->thread_lock);
|
---|
409 | cs_debug_mask(D_TRACE, "add %s job action %d queue length %d %s",
|
---|
410 | action > ACTION_CLIENT_FIRST ? "client" : "reader", action,
|
---|
411 | ll_count(cl->joblist), username(cl));
|
---|
412 | return 1;
|
---|
413 | }
|
---|
414 |
|
---|
415 | pthread_attr_t attr;
|
---|
416 | pthread_attr_init(&attr);
|
---|
417 | /* pcsc doesn't like this either; segfaults on x86, x86_64 */
|
---|
418 | struct s_reader *rdr = cl->reader;
|
---|
419 | if (cl->typ != 'r' || !rdr || rdr->typ != R_PCSC)
|
---|
420 | pthread_attr_setstacksize(&attr, PTHREAD_STACK_SIZE);
|
---|
421 |
|
---|
422 | if (action != ACTION_READER_CHECK_HEALTH) {
|
---|
423 | cs_debug_mask(D_TRACE, "start %s thread action %d",
|
---|
424 | action > ACTION_CLIENT_FIRST ? "client" : "reader", action);
|
---|
425 | }
|
---|
426 |
|
---|
427 | int32_t ret = pthread_create(&cl->thread, &attr, work_thread, (void *)data);
|
---|
428 | if (ret) {
|
---|
429 | cs_log("ERROR: can't create thread for %s (errno=%d %s)",
|
---|
430 | action > ACTION_CLIENT_FIRST ? "client" : "reader", ret, strerror(ret));
|
---|
431 | free_job_data(data);
|
---|
432 | } else {
|
---|
433 | pthread_detach(cl->thread);
|
---|
434 | }
|
---|
435 | pthread_attr_destroy(&attr);
|
---|
436 |
|
---|
437 | cl->thread_active = 1;
|
---|
438 | pthread_mutex_unlock(&cl->thread_lock);
|
---|
439 | return 1;
|
---|
440 | }
|
---|