SASC-NG
DVBSoftCAM

plugin_scan.c

Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines