|
SASC
DVBSoftCAM
|
00001 /* 00002 DVBLoopback - plugin_scan.c 00003 Copyright AxesOfEvil 2007 00004 00005 This file is part of DVBLoopback. 00006 00007 DVBLoopback is free software; you can redistribute it and/or modify 00008 it under the terms of the GNU General Public License as published by 00009 the Free Software Foundation; either version 2 of the License, or 00010 (at your option) any later version. 00011 00012 DVBLoopback is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 GNU General Public License for more details. 00016 00017 You should have received a copy of the GNU General Public License 00018 along with DVBLoopback; if not, write to the Free Software 00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 #include <stdlib.h> 00022 #include <stdio.h> 00023 #include <fcntl.h> 00024 #include <pthread.h> 00025 #include <sys/mman.h> 00026 #include <sys/poll.h> 00027 #include <sys/ioctl.h> 00028 #include <sys/types.h> 00029 #include <sys/stat.h> 00030 #include <sys/wait.h> 00031 #include <signal.h> 00032 #include <string.h> 00033 #include <getopt.h> 00034 #include <unistd.h> 00035 #include <errno.h> 00036 #include "list.h" 00037 00038 #include <linux/dvb/dmx.h> 00039 #include <linux/dvb/frontend.h> 00040 #include "process_req.h" 00041 #include "msg_passing.h" 00042 #include "plugin_getsid.h" 00043 #include "plugin_msg.h" 00044 00045 #define PLUGIN_ID 30 00046 #define DBG_NAME "Scanner" 00047 #include "debug.h" 00048 00049 static LIST_HEAD(scanner_list); 00050 00051 static int scan_opt = 0; 00052 static int scan_ports = 0; 00053 static struct option Scan_Opts[] = { 00054 {"scan-ports", 1, &scan_opt, 'p'}, 00055 {0, 0, 0, 0}, 00056 }; 00057 //SCAN_MIN_WAITTIME: minimum time between an app closing the frontend and the scanner starting 00058 #define SCAN_MIN_WAITTIME (10) 00059 //SCAN_WAITTIME: time between completing a scan on the current port and starting it again 00060 #define SCAN_WAITTIME (10*60) 00061 //SCAN_TIMEOUT: time to watch a specific port for updates if the keys can't be found 00062 #define SCAN_TIMEOUT (30*60) 00063 //SCAN_MIN_TUNETIME: Minimum time to wait before switching to next port (only used after 00064 // Each port has successfully tuned once 00065 #define SCAN_MIN_TUNETIME (10*60) 00066 //SCAN_CAM_MINTIME: Minimum time between even/odd keys for them to be considered valid. 00067 // Don't play with this unless you know what you are doing 00068 #define SCAN_CAM_MINTIME 5 00069 00070 #define MAX_PORTS 16 00071 00072 00073 extern char * get_camdir(); 00074 static char scan_script[256], scan_lib[256]; 00075 00076 struct scanner { 00077 struct list_head list; 00078 struct poll_ll *fe_fdptr; 00079 int fecount; 00080 int has_tuned; 00081 int port; 00082 int pid; 00083 int waiting; 00084 int done; 00085 struct parser_adpt *pc_all; 00086 time_t lastcmd; 00087 time_t starttime[MAX_PORTS]; 00088 time_t found[MAX_PORTS]; 00089 struct { 00090 int state; 00091 int last_pol; 00092 time_t lasttime; 00093 } ca[8]; 00094 }; 00095 unsigned char boo[1024]; 00096 static pthread_mutex_t scanner_mutex = PTHREAD_MUTEX_INITIALIZER; 00097 static pthread_cond_t scanner_cond = PTHREAD_COND_INITIALIZER; 00098 static pthread_t scanner_thread; 00099 00100 static struct scanner *find_scanner_from_pc(struct parser_cmds *pc) 00101 { 00102 struct list_head *ptr; 00103 list_for_each(ptr, &scanner_list) { 00104 struct scanner *scanner = list_entry(ptr, struct scanner); 00105 if(scanner->pc_all->frontend->common == pc->common) 00106 return scanner; 00107 } 00108 return NULL; 00109 } 00110 00111 void * scan_loop(void *arg) 00112 { 00113 struct scanner *scanner; 00114 struct list_head *ptr; 00115 time_t curtime; 00116 pthread_mutex_lock(&scanner_mutex); 00117 while(1) { 00118 int ok = 0; 00119 while(! ok) { 00120 curtime = time(NULL); 00121 list_for_each(ptr, &scanner_list) { 00122 scanner = list_entry(ptr, struct scanner); 00123 if(scanner->fecount==0 && curtime - scanner->lastcmd >= SCAN_MIN_WAITTIME) { 00124 ok = 1; 00125 break; 00126 } 00127 } 00128 if(! ok) { 00129 pthread_cond_wait(&scanner_cond, &scanner_mutex); 00130 } 00131 } 00132 //We can start the scanner if we get here 00133 if(! scanner->pid) { 00134 if(curtime > scanner->starttime[scanner->port] + SCAN_WAITTIME) { 00135 //Time to try scanning the current port 00136 scanner->done = 0; 00137 for(int i = 0; i < 8; i++) { 00138 scanner->ca[i].lasttime = curtime; 00139 scanner->ca[i].state = 0; 00140 } 00141 dprintf0("Starting scanner on port %d\n", scanner->port); 00142 if(0 == (scanner->pid = fork())) { 00143 char env[256], *env1[3] = {env, (char *) 0}; 00144 struct { char a[8], b[8], c[8];} arg = {"scanner", "", ""}; 00145 char *arg1[] = {arg.a, arg.b, arg.c, (char *) 0}; 00146 snprintf(env,sizeof(env), "LD_PRELOAD=%s", scan_lib); 00147 snprintf(arg.b, 8, "%d", scanner->pc_all->frontend->common->virt_adapt); 00148 snprintf(arg.c, 8, "%d", scanner->port); 00149 int fd = open("/dev/null", O_WRONLY); 00150 while (dup2(fd, 1) < 0 && errno == EINTR) 00151 ; 00152 while (dup2(fd, 2) < 0 && errno == EINTR) 00153 ; 00154 while (close(fd) < 0 && errno == EINTR) 00155 ; 00156 execve(scan_script, arg1, env1); 00157 exit(0); 00158 } 00159 scanner->starttime[scanner->port] = curtime; 00160 } 00161 } else if(waitpid(scanner->pid, NULL, WNOHANG)) { 00162 scanner->pid = 0; 00163 scanner->port = (scanner->port+1) % scan_ports; 00164 scanner->waiting = 0; 00165 } else if(! scanner->waiting && 00166 ( (scanner->done 00167 && (! scanner->found[scanner->port] 00168 || curtime > scanner->starttime[scanner->port] + SCAN_MIN_TUNETIME) 00169 ) 00170 || curtime > scanner->starttime[scanner->port] + SCAN_TIMEOUT 00171 )) { 00172 if(scanner->done) { 00173 dprintf0("Scan complete for port %d. Terminating %d\n", scanner->port, scanner->pid); 00174 scanner->found[scanner->port] = 1; 00175 } 00176 kill(scanner->pid, 9); 00177 scanner->waiting = 1; 00178 } 00179 scanner->lastcmd = curtime; 00180 } 00181 } 00182 static void process_scan(struct msg *msg, unsigned int priority) 00183 { 00184 if (msg->type == MSG_TRYSCAN) { 00185 msg->type = MSG_PROCESSED; 00186 pthread_mutex_lock(&scanner_mutex); 00187 pthread_cond_signal(&scanner_cond); 00188 pthread_mutex_unlock(&scanner_mutex); 00189 } else if(msg->type == MSG_CAMUPDATE) { 00190 struct scanner *scanner; 00191 time_t curtime = time(NULL); 00192 int idx = (((unsigned long)msg->data)& 0xff) >> 1; 00193 int polarity = ((unsigned long)msg->data)& 0x01; 00194 msg->type = MSG_PROCESSED; 00195 ll_find_elem(scanner, scanner_list, pc_all->frontend->common->virt_adapt, msg->id, struct scanner); 00196 if(scanner == NULL || ! scanner->pid || scanner->waiting) 00197 return; 00198 if(polarity != scanner->ca[idx].last_pol && 00199 curtime - scanner->ca[idx].lasttime > SCAN_CAM_MINTIME) { 00200 if(scanner->ca[idx].state < 4) 00201 scanner->ca[idx].state++; 00202 if(scanner->ca[idx].state == 4) { 00203 pthread_mutex_lock(&scanner_mutex); 00204 scanner->done = 1; 00205 pthread_mutex_unlock(&scanner_mutex); 00206 } 00207 } else { 00208 pthread_mutex_lock(&scanner_mutex); 00209 scanner->done = 0; 00210 pthread_mutex_unlock(&scanner_mutex); 00211 scanner->ca[idx].state = 0; 00212 } 00213 scanner->ca[idx].last_pol = polarity; 00214 scanner->ca[idx].lasttime = curtime; 00215 dprintf0("id:%d idx: %d state:%d pol:%d done:%d\n", msg->id, idx, scanner->ca[idx].state, polarity, scanner->done); 00216 } 00217 } 00218 00219 static void fe_open_pre(struct parser_cmds *pc, struct poll_ll *fdptr, 00220 cmdret_t *result, int *ret, 00221 unsigned long int cmd, unsigned char *data) 00222 { 00223 struct scanner *scanner; 00224 scanner = find_scanner_from_pc(pc); 00225 if(scanner == NULL) 00226 return; 00227 00228 if((fdptr->flags & O_ACCMODE) == O_RDONLY) 00229 return; 00230 00231 pthread_mutex_lock(&scanner_mutex); 00232 if(scanner->fe_fdptr) { 00233 if((fdptr->flags & (O_SYNC | O_ASYNC)) != (O_SYNC | O_ASYNC)) { 00234 //This isn't the scanner fd so kill the scanner 00235 if(scanner->pid && ! scanner->waiting) { 00236 kill(scanner->pid, 9); 00237 scanner->waiting = 1; 00238 } 00239 close(scanner->fe_fdptr->fd); 00240 scanner->fe_fdptr->fd = open("/dev/null", O_RDWR); 00241 scanner->fe_fdptr = NULL; 00242 scanner->lastcmd = time(NULL); 00243 } 00244 } 00245 pthread_mutex_unlock(&scanner_mutex); 00246 } 00247 00248 static void fe_open_post(struct parser_cmds *pc, struct poll_ll *fdptr, 00249 cmdret_t *result, int *ret, 00250 unsigned long int cmd, unsigned char *data) 00251 { 00252 struct scanner *scanner; 00253 scanner = find_scanner_from_pc(pc); 00254 if(scanner == NULL) 00255 return; 00256 00257 if((fdptr->flags & O_ACCMODE) == O_RDONLY) 00258 return; 00259 if(fdptr->fd < 0) 00260 return; 00261 pthread_mutex_lock(&scanner_mutex); 00262 if((fdptr->flags & (O_SYNC | O_ASYNC)) == (O_SYNC | O_ASYNC)) { 00263 // This is the scanner fd 00264 scanner->fe_fdptr = fdptr; 00265 } else { 00266 scanner->fecount++; 00267 scanner->lastcmd = time(NULL); 00268 } 00269 pthread_mutex_unlock(&scanner_mutex); 00270 } 00271 00272 static void fe_close(struct parser_cmds *pc, struct poll_ll *fdptr, 00273 cmdret_t *result, int *ret, 00274 unsigned long int cmd, unsigned char *data) 00275 { 00276 struct scanner *scanner; 00277 scanner = find_scanner_from_pc(pc); 00278 if(scanner == NULL) 00279 return; 00280 00281 if((fdptr->flags & O_ACCMODE) == O_RDONLY) 00282 return; 00283 00284 pthread_mutex_lock(&scanner_mutex); 00285 if((fdptr->flags & (O_SYNC | O_ASYNC)) == (O_SYNC | O_ASYNC)) { 00286 // This is the scanner fd 00287 scanner->fe_fdptr = NULL; 00288 } else { 00289 if(scanner->fecount > 0) 00290 scanner->fecount--; 00291 scanner->lastcmd = time(NULL); 00292 } 00293 pthread_mutex_unlock(&scanner_mutex); 00294 } 00295 00296 #define FIND_CMD(result, adpt, type, _id) do { \ 00297 struct cmd_list *cmd; \ 00298 ll_find_elem(cmd, adpt->pre_##type, id, (_id), struct cmd_list); \ 00299 if(! cmd) \ 00300 ll_find_elem(cmd, adpt->post_##type, id, (_id), struct cmd_list); \ 00301 if(! cmd) \ 00302 result = NULL;\ 00303 else \ 00304 result = cmd->cmd; \ 00305 } while(0); 00306 00307 static void launch_scan() 00308 { 00309 if(scan_ports) { 00310 pthread_create(&scanner_thread, &default_attr, scan_loop, NULL); 00311 msg_send_replace(MSG_LOW_PRIORITY, MSG_TRYSCAN, 0, NULL, 00312 10, MSG_RECURRING); 00313 } 00314 } 00315 00316 static void connect_scan(struct parser_adpt *pc_all) 00317 { 00318 struct scanner *scanner; 00319 struct stat st; 00320 scanner = (struct scanner *)malloc(sizeof(struct scanner)); 00321 if(! scanner) 00322 return; 00323 bzero(scanner, sizeof(struct scanner)); 00324 if(scan_ports) { 00325 snprintf(scan_script, sizeof(scan_script), "%s/scanner", get_camdir()); 00326 snprintf(scan_lib, sizeof(scan_lib), "%s/libscanwrap.so", get_camdir()); 00327 if(stat(scan_script, &st) == -1 || ! (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { 00328 dprintf0("Couldn't locate executable %s\n", scan_script); 00329 exit(1); 00330 } 00331 if(stat(scan_lib, &st) == -1) { 00332 dprintf0("Couldn't locate wrapper lib %s\n", scan_lib); 00333 exit(1); 00334 } 00335 } 00336 scanner->pc_all = pc_all; 00337 scanner->lastcmd = time(NULL); 00338 ATTACH_CALLBACK(&pc_all->frontend->pre_open, fe_open_pre, -1); 00339 ATTACH_CALLBACK(&pc_all->frontend->post_open, fe_open_post, -1); 00340 ATTACH_CALLBACK(&pc_all->frontend->post_close, fe_close, -1); 00341 list_add_tail(&scanner->list, &scanner_list); 00342 } 00343 00344 static struct option *parseopt_scan(arg_enum_t cmd) 00345 { 00346 if(cmd == ARG_INIT) { 00347 return Scan_Opts; 00348 } 00349 if(cmd == ARG_HELP) { 00350 printf(" --scan-ports <num_ports>: Turn on scanner\n"); 00351 } 00352 if(! scan_opt) 00353 return NULL; 00354 00355 switch(scan_opt) { 00356 case 'p': 00357 scan_ports = strtol(optarg, NULL, 0); 00358 dprintf0("Enabling scanner on %d ports\n", scan_ports); 00359 break; 00360 } 00361 //must reset scan_opt after every call 00362 scan_opt = 0; 00363 return NULL; 00364 } 00365 00366 //list, plugin_id, name, parse_args, connect, launch, message, send_msg 00367 static struct plugin_cmd plugin_cmds = {{NULL, NULL}, PLUGIN_ID, "scanner", 00368 parseopt_scan, connect_scan, launch_scan, process_scan, NULL, NULL, NULL}; 00369 00370 int __attribute__((constructor)) __scan_init(void) 00371 { 00372 // communicates the existance of teh current plugin to the main program. 00373 if(1) list_add(&plugin_cmds.list, &plugin_cmdlist); 00374 return 0; 00375 }