SASC
DVBSoftCAM

log.c

Go to the documentation of this file.
00001 /*
00002  * Softcam plugin to VDR (C++)
00003  *
00004  * This code is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This code is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
00018  */
00019 
00020 #include <stdlib.h>
00021 #include <stdio.h>
00022 #include <stdarg.h>
00023 #include <time.h>
00024 #include <sys/time.h>
00025 
00026 #include <vdr/tools.h>
00027 #include <vdr/thread.h>
00028 
00029 #include "log.h"
00030 #include "misc.h"
00031 
00032 #define LMOD_SUP 32
00033 #define LMOD_CFG_VALID  0x80000000
00034 
00035 struct LogConfig logcfg = {
00036   1,0,0,0,0,
00037   0,
00038   "/var/log/vdr-sc"
00039   };
00040 
00041 static const struct LogModule lm_general = {
00042   (LMOD_ENABLE|L_GEN_ALL)&LOPT_MASK,
00043   (LMOD_ENABLE|L_GEN_ALL)&LOPT_MASK,
00044   "general", { "error","warn","info","debug","misc" }
00045   };
00046 
00047 static const struct LogModule *mods[LMOD_SUP] = { &lm_general };
00048 static int config[LMOD_SUP] = { LMOD_ENABLE|L_GEN_ALL|LMOD_CFG_VALID };
00049 static int cmask[LMOD_SUP]  = { LMOD_ENABLE|L_GEN_ALL };
00050 static cMutex logfileMutex;
00051 static FILE *logfile=0;
00052 static bool logfileShutup=false, logfileReopen=false;
00053 static long long logfileSize;
00054 
00055 static unsigned int lastCrc=0;
00056 static int lastC=0, lastCount=0;
00057 static cTimeMs lastTime;
00058 static cMutex lastMutex;
00059 
00060 // -- cLogging -----------------------------------------------------------------
00061 
00062 void (*cLogging::LogPrint)(const struct LogHeader *lh, const char *txt)=cLogging::PrivateLogPrint;
00063 
00064 bool cLogging::AddModule(int m, const struct LogModule *lm)
00065 {
00066   if(m<LMOD_SUP) {
00067     if(mods[m]) Printf(L_GEN_DEBUG,"duplicate logging module id %d (%s & %s)",m,mods[m]->Name,lm->Name);
00068     mods[m]=lm;
00069     if(config[m]&LMOD_CFG_VALID) UpgradeOptions(m);
00070     else SetModuleDefault(LCLASS(m,0));
00071 //printf("module %-16s added. mod=%x supp=%08x def=%08x cfg=%08x\n",lm->Name,m,lm->OptSupported,lm->OptDefault,config[m]);
00072     }
00073   else Printf(L_GEN_DEBUG,"failed to add logging module %d (%s)",m,lm->Name);
00074   return true;
00075 }
00076 
00077 void cLogging::SetLogPrint(void (*lp)(const struct LogHeader *lh, const char *txt))
00078 {
00079   LogPrint=lp;
00080 }
00081 
00082 const struct LogModule *cLogging::GetModule(int c)
00083 {
00084   int m=LMOD(c);
00085   return m<LMOD_SUP ? mods[m] : 0;
00086 }
00087 
00088 bool cLogging::GetHeader(int c, struct LogHeader *lh)
00089 {
00090   const struct LogModule *lm=GetModule(c);
00091   if(lm) {
00092     if(!logcfg.noTimestamp) {
00093       struct timeval t;
00094       gettimeofday(&t,NULL);
00095       struct tm tm_r;
00096       int q=strftime(lh->stamp,sizeof(lh->stamp),"%b %e %T",localtime_r(&t.tv_sec,&tm_r));
00097       snprintf(lh->stamp+q,sizeof(lh->stamp)-q,".%03u",(unsigned int)(t.tv_usec/1000));
00098       }
00099     else lh->stamp[0]=0;
00100     int i, o=LOPT(c)&~LMOD_ENABLE;
00101     for(i=0; i<LOPT_NUM; i++,o>>=1) if(o&1) break;
00102     snprintf(lh->tag,sizeof(lh->tag),"%s.%s",lm->Name,(i>=1 && i<LOPT_NUM && lm->OptName[i-1])?lm->OptName[i-1]:"unknown");
00103     lh->c=c;
00104     return true;
00105     }
00106   return false;  
00107 }
00108 
00109 void cLogging::LogLine(const struct LogHeader *lh, const char *txt, bool doCrc)
00110 {
00111   if(doCrc) {
00112     unsigned int crc=crc32_le(0,(const unsigned char *)txt,strlen(txt)+1);
00113     lastMutex.Lock();
00114     if(lastCrc==crc && lastC==lh->c && ++lastCount<4000000 && lastTime.Elapsed()<=30*1000) {
00115       lastMutex.Unlock();
00116       return;
00117       }
00118     else {
00119       unsigned int saveCrc=lastCrc;
00120       int saveCount=lastCount, saveC=lastC;
00121       lastTime.Set();
00122       lastCrc=crc; lastC=lh->c; lastCount=0;
00123       lastMutex.Unlock();
00124       if(saveCount>1) {
00125         struct LogHeader lh2;
00126         if(GetHeader(saveC,&lh2)) {
00127           char buff[128];
00128           snprintf(buff,sizeof(buff),"last message repeated %d times",saveCount);
00129           LogLine(&lh2,buff,false);
00130           }
00131         if(saveCrc==crc && saveC==lh->c) return;
00132         }
00133       }
00134     }
00135 
00136   LogPrint(lh,txt);
00137 }
00138 
00139 void cLogging::PrivateLogPrint(const struct LogHeader *lh, const char *txt)
00140 {
00141   if(logcfg.logFile) {
00142     logfileMutex.Lock();
00143     if(logfileReopen) {
00144       logfileReopen=false; logfileShutup=false;
00145       if(logfile) {
00146         PRINTF(L_GEN_DEBUG,"logfile closed, reopen as '%s'",logcfg.logFilename);
00147         fclose(logfile);
00148         logfile=0;
00149         }
00150       }
00151     if(!logfile && !logfileShutup) {
00152       logfile=fopen(logcfg.logFilename,"a");
00153       if(logfile) {
00154         setlinebuf(logfile);
00155         logfileSize=ftell(logfile);
00156         if(logfileSize<0) {
00157           logfileSize=0;
00158           PRINTF(L_GEN_ERROR,"cannot determine size of logfile '%s', assuming zero",logcfg.logFilename);
00159           }
00160         PRINTF(L_GEN_DEBUG,"logfile '%s' opened",logcfg.logFilename);
00161         }
00162       else {
00163         logfileShutup=true;
00164         PRINTF(L_GEN_ERROR,"failed to open logfile '%s': %s",logcfg.logFilename,strerror(errno));
00165         }
00166       }
00167     if(logfile) {
00168       int q=fprintf(logfile,"%s [%s] %s\n",lh->stamp,lh->tag,txt);
00169       if(q>0) logfileSize+=q;
00170 
00171       if(logcfg.maxFilesize>0 && logfileSize>((long long)logcfg.maxFilesize*1024)) {
00172         fprintf(logfile,"%s [%s] %s\n",lh->stamp,lh->tag,"logfile closed, filesize limit reached");
00173         fclose(logfile);
00174         logfile=0; logfileShutup=false;
00175         char *name=bprintf("%s.old",logcfg.logFilename);
00176         if(rename(logcfg.logFilename,name)) {
00177           logfileShutup=true;
00178           PRINTF(L_GEN_ERROR,"failed to rotate logfile: %s",strerror(errno));
00179           PRINTF(L_GEN_ERROR,"logging to file disabled!");
00180           }
00181         free(name);
00182         }
00183       }
00184     logfileMutex.Unlock();
00185     }
00186 
00187   if(logcfg.logCon)
00188     printf("%s [%s] %s\n",lh->stamp,lh->tag,txt);
00189 
00190   if(logcfg.logSys || LMOD(lh->c)==L_GEN) {
00191     int pri=-1;
00192     switch(lh->c) {
00193       case L_GEN_ERROR:
00194       case L_GEN_WARN:  if(SysLogLevel>0) pri=LOG_ERR; break;
00195       case L_GEN_INFO:  if(SysLogLevel>1) pri=LOG_INFO; break;
00196       case L_GEN_DEBUG: if(SysLogLevel>2) pri=LOG_DEBUG; break;
00197       case L_GEN_MISC:  if(logcfg.logSys) pri=LOG_DEBUG; break;
00198       default:          pri=LOG_DEBUG; break;
00199       }
00200     if(pri>=0)
00201       syslog(pri,"[%d] [%s] %s",cThread::ThreadId(),lh->tag,txt);
00202     }
00203 }
00204 
00205 bool cLogging::Enabled(int c)
00206 {
00207   int m=LMOD(c);
00208   int o=LOPT(c)|LMOD_CFG_VALID|LMOD_ENABLE;
00209   return m<LMOD_SUP && (config[m] & o)==o;
00210 }
00211 
00212 void cLogging::Printf(int c, const char *format, ...)
00213 {
00214   if(Enabled(c)) {
00215     struct LogHeader lh;
00216     if(GetHeader(c,&lh)) {
00217       char buff[1024];
00218       va_list ap;
00219       va_start(ap,format);
00220       vsnprintf(buff,sizeof(buff),format,ap);
00221       va_end(ap);
00222       LogLine(&lh,buff);
00223       }
00224     }
00225 }
00226 
00227 void cLogging::Puts(int c, const char *txt)
00228 {
00229   if(txt && Enabled(c)) {
00230     struct LogHeader lh;
00231     if(GetHeader(c,&lh)) {
00232       LogLine(&lh,txt);
00233       }
00234     }
00235 }
00236 
00237 void cLogging::PutLB(int c, cLineBuff *lb)
00238 {
00239   if(lb->Length()>0) {
00240     Puts(c,lb->Line());
00241     lb->Flush();
00242     }
00243 }
00244 
00245 void cLogging::Dump(int c, const void *data, int n, const char *format, ...)
00246 {
00247   if(Enabled(c)) {
00248     struct LogHeader lh;
00249     if(GetHeader(c,&lh)) {
00250       char buff[1024];
00251       va_list ap;
00252       va_start(ap,format);
00253       vsnprintf(buff,sizeof(buff),format,ap);
00254       va_end(ap);
00255       LogLine(&lh,buff);
00256       const unsigned char *d=(const unsigned char *)data;
00257       for(int i=0; i<n;) {
00258         int q=sprintf(buff,"%04x:",i);
00259         for(int l=0 ; l<16 && i<n ; l++) q+=sprintf(&buff[q]," %02x",d[i++]);
00260         LogLine(&lh,buff);
00261         }
00262       }
00263     }
00264 }
00265 
00266 void cLogging::LineDump(int c, const void *data, int n, const char *format, ...)
00267 {
00268   if(Enabled(c)) {
00269     struct LogHeader lh;
00270     if(GetHeader(c,&lh)) {
00271       char buff[4096];
00272       va_list ap;
00273       va_start(ap,format);
00274       unsigned int q=vsnprintf(buff,sizeof(buff),format,ap);
00275       va_end(ap);
00276       const unsigned char *d=(const unsigned char *)data;
00277       for(int i=0; i<n; i++) {
00278         if(q>=sizeof(buff)-8) {
00279           q+=snprintf(&buff[q],sizeof(buff)-q,"....");
00280           break;
00281           }
00282         q+=snprintf(&buff[q],sizeof(buff)-q," %02x",d[i]);
00283         }
00284       LogLine(&lh,buff);
00285       }
00286     }
00287 }
00288 
00289 void cLogging::SetModuleDefault(int c)
00290 {
00291   const struct LogModule *lm=GetModule(c);
00292   if(lm) {
00293     int m=LMOD(c);
00294     config[m]=(lm->OptDefault&LOPT_MASK)|LMOD_CFG_VALID;
00295     cmask[m] = lm->OptSupported&LOPT_MASK;
00296     }
00297 }
00298 
00299 void cLogging::SetModuleOptions(int c)
00300 {
00301   const struct LogModule *lm=GetModule(c);
00302   if(lm) config[LMOD(c)]=(LOPT(c)&(lm->OptSupported&LOPT_MASK))|LMOD_CFG_VALID;
00303 }
00304 
00305 int cLogging::GetModuleOptions(int c)
00306 {
00307   const struct LogModule *lm=GetModule(c);
00308   return lm ? LOPT(config[LMOD(c)]) : -1;
00309 }
00310 
00311 void cLogging::SetModuleOption(int c, bool on)
00312 {
00313   const struct LogModule *lm=GetModule(c);
00314   if(lm) {
00315     int m=LMOD(c);
00316     int o=LOPT(c)&(lm->OptSupported&LOPT_MASK);
00317     if(o) {
00318       if(on) config[m]|=o; else config[m]&=(~o);
00319       config[m]|=LMOD_CFG_VALID;
00320       }
00321     }
00322 }
00323 
00324 void cLogging::UpgradeOptions(int m)
00325 {
00326   const struct LogModule *lm=mods[m];
00327   if(lm) {
00328     config[m]&=(lm->OptSupported&LOPT_MASK);
00329     config[m]|=(lm->OptDefault&LOPT_MASK)&(~cmask[m]);
00330     cmask[m]  =lm->OptSupported&LOPT_MASK;
00331     config[m]|=LMOD_CFG_VALID;
00332     }
00333 }
00334 
00335 const char *cLogging::GetModuleName(int c)
00336 {
00337   const struct LogModule *lm=GetModule(c);
00338   return lm ? lm->Name : 0;
00339 }
00340 
00341 const char *cLogging::GetOptionName(int c)
00342 {
00343   const struct LogModule *lm=GetModule(c);
00344   if(lm) {
00345     int o=LOPT(c)&~LMOD_ENABLE;
00346     if(lm->OptSupported & o) {
00347       int i;
00348       for(i=0; i<LOPT_NUM; i++,o>>=1) if(o&1) break;
00349       return (i>=1 && i<LOPT_NUM && lm->OptName[i-1]) ? lm->OptName[i-1] : 0;
00350       }
00351     }
00352   return 0;
00353 }
00354 
00355 bool cLogging::GetConfig(cLineBuff *lb)
00356 {
00357   lb->Flush();
00358   bool cont=false;
00359   for(int m=1; m<LMOD_SUP; m++)
00360     if(config[m]&LMOD_CFG_VALID) {
00361       lb->Printf("%s%d:%x:%x",cont?",":"",m,config[m]&~LMOD_CFG_VALID,cmask[m]);
00362       cont=true;
00363       }
00364   return cont;
00365 }
00366 
00367 void cLogging::ParseConfig(const char *txt)
00368 {
00369   int n=0, l, m, o, d;
00370   while(sscanf(&txt[n],"%d:%x:%x%n",&m,&o,&d,&l)==3) {
00371     if(m>=1 && m<LMOD_SUP) {
00372       config[m]=(o&LOPT_MASK)|LMOD_CFG_VALID;
00373       cmask[m] =(d&LOPT_MASK);
00374       UpgradeOptions(m);
00375       }
00376     n+=l;
00377     if(txt[n]!=',') break;
00378     n++;
00379     }
00380 }
00381 
00382 void cLogging::ReopenLogfile(void)
00383 {
00384   logfileMutex.Lock();
00385   logfileReopen=true;
00386   logfileMutex.Unlock();
00387 }
00388 
00389 int cLogging::GetClassByName(const char *name)
00390 {
00391   const char *s=index(name,'.');
00392   if(s) {
00393     unsigned int l=s-name;
00394     for(int m=1; m<LMOD_SUP; m++) {
00395       const struct LogModule *lm=mods[m];
00396       if(lm && strlen(lm->Name)==l && !strncasecmp(name,lm->Name,l)) {
00397         s++;
00398         if(!strcasecmp(s,"enable"))
00399            return LCLASS(m,1);
00400         for(int o=1; o<LOPT_NUM; o++)
00401           if((lm->OptSupported&(1<<o)) && lm->OptName[o-1] && !strcasecmp(s,lm->OptName[o-1]))
00402             return LCLASS(m,1<<o);
00403         break;
00404         }
00405       }
00406     }
00407   return -1;  
00408 }
00409 
00410 // -- cUserMsg -----------------------------------------------------------------
00411 
00412 cUserMsg::cUserMsg(const char *m)
00413 {
00414   msg=strdup(m);
00415 }
00416 
00417 cUserMsg::~cUserMsg()
00418 {
00419   free(msg);
00420 }
00421 
00422 // -- cUserMsgs ----------------------------------------------------------------
00423 
00424 cUserMsgs ums;
00425 
00426 cUserMsgs::cUserMsgs(void)
00427 {
00428   mutex=new cMutex;
00429 }
00430 
00431 cUserMsgs::~cUserMsgs()
00432 {
00433   delete mutex;
00434 }
00435 
00436 void cUserMsgs::Queue(const char *fmt, ...)
00437 {
00438   if(logcfg.logUser) {
00439     char buff[1024];
00440     va_list ap;
00441     va_start(ap,fmt);
00442     vsnprintf(buff,sizeof(buff),fmt,ap);
00443     va_end(ap);
00444     mutex->Lock();
00445     Add(new cUserMsg(buff));
00446     mutex->Unlock();
00447     }
00448 }
00449 
00450 cUserMsg *cUserMsgs::GetQueuedMsg(void)
00451 {
00452   mutex->Lock();
00453   cUserMsg *um=First();
00454   if(um) Del(um,false);
00455   mutex->Unlock();
00456   return um;
00457 }
00458 
00459 // -- cLogLineBuff -------------------------------------------------------------
00460 
00461 cLogLineBuff::cLogLineBuff(int C)
00462 :cLineBuff(256)
00463 {
00464   c=C;
00465 }
00466 
00467 cLogLineBuff::~cLogLineBuff()
00468 {
00469   Flush();
00470 }
00471 
00472 void cLogLineBuff::Flush(void)
00473 {
00474   cLogging::PutLB(c,this);
00475 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines