[8] | 1 | #include "globals.h"
|
---|
| 2 | #include <syslog.h>
|
---|
[7658] | 3 | #include "module-monitor.h"
|
---|
[7576] | 4 | #include "oscam-client.h"
|
---|
[7570] | 5 | #include "oscam-garbage.h"
|
---|
[7583] | 6 | #include "oscam-lock.h"
|
---|
[7647] | 7 | #include "oscam-log.h"
|
---|
[7582] | 8 | #include "oscam-net.h"
|
---|
[7585] | 9 | #include "oscam-string.h"
|
---|
[7644] | 10 | #include "oscam-time.h"
|
---|
[8] | 11 |
|
---|
[8178] | 12 | extern char *syslog_ident;
|
---|
[8292] | 13 | extern int32_t exit_oscam;
|
---|
[7610] | 14 |
|
---|
[6069] | 15 | char *LOG_LIST = "log_list";
|
---|
| 16 |
|
---|
[8] | 17 | static FILE *fp=(FILE *)0;
|
---|
| 18 | static FILE *fps=(FILE *)0;
|
---|
[5440] | 19 | static int8_t logStarted = 0;
|
---|
[8275] | 20 | static LLIST *log_list;
|
---|
[8] | 21 |
|
---|
[6062] | 22 | struct s_log {
|
---|
| 23 | char *txt;
|
---|
| 24 | int8_t header_len;
|
---|
[6069] | 25 | int8_t direct_log;
|
---|
[6215] | 26 | int8_t cl_typ;
|
---|
| 27 | char *cl_usr;
|
---|
| 28 | char *cl_text;
|
---|
[6062] | 29 | };
|
---|
| 30 |
|
---|
[7156] | 31 | #if defined(WEBIF) || defined(MODULE_MONITOR)
|
---|
[8275] | 32 | static CS_MUTEX_LOCK loghistory_lock;
|
---|
[8281] | 33 | char *loghist = NULL; // ptr of log-history
|
---|
| 34 | char *loghistptr = NULL;
|
---|
[6309] | 35 | #endif
|
---|
[5440] | 36 |
|
---|
[5667] | 37 | #define LOG_BUF_SIZE 512
|
---|
[4195] | 38 |
|
---|
[4994] | 39 | static void switch_log(char* file, FILE **f, int32_t (*pfinit)(void))
|
---|
[8] | 40 | {
|
---|
[6323] | 41 | if(cfg.max_log_size && file) //only 1 thread needs to switch the log; even if anticasc, statistics and normal log are running
|
---|
[3953] | 42 | //at the same time, it is ok to have the other logs switching 1 entry later
|
---|
[1762] | 43 | {
|
---|
[6094] | 44 | if(*f != NULL && ftell(*f) >= cfg.max_log_size*1024) {
|
---|
[4994] | 45 | int32_t rc;
|
---|
[4382] | 46 | char prev_log[strlen(file) + 6];
|
---|
[7156] | 47 | snprintf(prev_log, sizeof(prev_log), "%s-prev", file);
|
---|
[5440] | 48 | fprintf(*f, "switch log file\n");
|
---|
| 49 | fflush(*f);
|
---|
| 50 | fclose(*f);
|
---|
| 51 | *f = (FILE *)0;
|
---|
| 52 | rc = rename(file, prev_log);
|
---|
| 53 | if( rc!=0 ) {
|
---|
| 54 | fprintf(stderr, "rename(%s, %s) failed (errno=%d %s)\n", file, prev_log, errno, strerror(errno));
|
---|
| 55 | }
|
---|
| 56 | else
|
---|
| 57 | if( pfinit()){
|
---|
[7307] | 58 | fprintf(stderr, "Initialisation of log file failed, continuing without logging thread %8lX. Log will be output to stdout!", (unsigned long)pthread_self());
|
---|
[5440] | 59 | cfg.logtostdout = 1;
|
---|
[3502] | 60 | }
|
---|
[1762] | 61 | }
|
---|
| 62 | }
|
---|
[8] | 63 | }
|
---|
| 64 |
|
---|
[6069] | 65 | static void cs_write_log(char *txt, int8_t do_flush)
|
---|
[8] | 66 | {
|
---|
[5728] | 67 | // filter out entries with leading 's' and forward to statistics
|
---|
| 68 | if(txt[0] == 's') {
|
---|
| 69 | if (fps) {
|
---|
| 70 | switch_log(cfg.usrfile, &fps, cs_init_statistics);
|
---|
[2078] | 71 | if (fps) {
|
---|
[5728] | 72 | fputs(txt + 1, fps); // remove the leading 's' and write to file
|
---|
[6069] | 73 | if (do_flush) fflush(fps);
|
---|
[2078] | 74 | }
|
---|
[5728] | 75 | }
|
---|
| 76 | } else {
|
---|
| 77 | if(!cfg.disablelog){
|
---|
| 78 | if (fp){
|
---|
[6062] | 79 | switch_log(cfg.logfile, &fp, cs_open_logfiles); // only call the switch code if lock = 1 is specified as otherwise we are calling it internally
|
---|
[5728] | 80 | if (fp) {
|
---|
| 81 | fputs(txt, fp);
|
---|
[6069] | 82 | if (do_flush) fflush(fp);
|
---|
[4485] | 83 | }
|
---|
[1691] | 84 | }
|
---|
[5728] | 85 | if(cfg.logtostdout){
|
---|
| 86 | fputs(txt+11, stdout);
|
---|
[6069] | 87 | if (do_flush) fflush(stdout);
|
---|
[5728] | 88 | }
|
---|
[1691] | 89 | }
|
---|
[5728] | 90 | }
|
---|
[8] | 91 | }
|
---|
| 92 |
|
---|
[6069] | 93 | static void cs_write_log_int(char *txt)
|
---|
| 94 | {
|
---|
[6391] | 95 | if(exit_oscam == 1) {
|
---|
| 96 | cs_write_log(txt, 1);
|
---|
| 97 | } else {
|
---|
[7590] | 98 | char *newtxt = cs_strdup(txt);
|
---|
| 99 | if (!newtxt)
|
---|
| 100 | return;
|
---|
| 101 | struct s_log *log;
|
---|
[7600] | 102 | if (!cs_malloc(&log, sizeof(struct s_log))) {
|
---|
[7590] | 103 | free(newtxt);
|
---|
| 104 | return;
|
---|
| 105 | }
|
---|
| 106 | log->txt = newtxt;
|
---|
[6391] | 107 | log->header_len = 0;
|
---|
| 108 | log->direct_log = 1;
|
---|
[7156] | 109 | ll_append(log_list, log);
|
---|
[6391] | 110 | }
|
---|
[6069] | 111 | }
|
---|
| 112 |
|
---|
[6901] | 113 | int32_t cs_open_logfiles(void)
|
---|
[8] | 114 | {
|
---|
[5167] | 115 | char *starttext;
|
---|
| 116 | if(logStarted) starttext = "log switched";
|
---|
| 117 | else starttext = "started";
|
---|
[6323] | 118 | if (!fp && cfg.logfile) { //log to file
|
---|
[4588] | 119 | if ((fp = fopen(cfg.logfile, "a+")) <= (FILE *)0) {
|
---|
[4485] | 120 | fp = (FILE *)0;
|
---|
[4934] | 121 | fprintf(stderr, "couldn't open logfile: %s (errno %d %s)\n", cfg.logfile, errno, strerror(errno));
|
---|
[4485] | 122 | } else {
|
---|
[6621] | 123 | setvbuf(fp, NULL, _IOFBF, 8*1024);
|
---|
[4485] | 124 | time_t t;
|
---|
| 125 | char line[80];
|
---|
| 126 | memset(line, '-', sizeof(line));
|
---|
| 127 | line[(sizeof(line)/sizeof(char)) - 1] = '\0';
|
---|
| 128 | time(&t);
|
---|
[6027] | 129 | if (!cfg.disablelog){
|
---|
| 130 | char buf[28];
|
---|
[6040] | 131 | cs_ctime_r(&t, buf);
|
---|
[6027] | 132 | fprintf(fp, "\n%s\n>> OSCam << cardserver %s at %s%s\n", line, starttext, buf, line);
|
---|
| 133 | }
|
---|
[4485] | 134 | }
|
---|
[1691] | 135 | }
|
---|
[4555] | 136 | // according to syslog docu: calling closelog is not necessary and calling openlog multiple times is safe
|
---|
| 137 | // We use openlog to set the default syslog settings so that it's possible to allow switching syslog on and off
|
---|
[8261] | 138 | openlog(syslog_ident, LOG_NDELAY | LOG_PID, LOG_DAEMON);
|
---|
[7156] | 139 |
|
---|
[8196] | 140 | cs_log_nolock(">> OSCam << cardserver %s, version " CS_VERSION ", build r" CS_SVN_VERSION " (" CS_TARGET ")", starttext);
|
---|
[4485] | 141 | return(fp <= (FILE *)0);
|
---|
[8] | 142 | }
|
---|
| 143 |
|
---|
[7156] | 144 | #if defined(WEBIF) || defined(MODULE_MONITOR)
|
---|
| 145 | /*
|
---|
[5076] | 146 | This function allows to reinit the in-memory loghistory with a new size.
|
---|
| 147 | */
|
---|
| 148 | void cs_reinit_loghist(uint32_t size)
|
---|
| 149 | {
|
---|
[7049] | 150 | char *tmp = NULL, *tmp2;
|
---|
[5076] | 151 | if(size != cfg.loghistorysize){
|
---|
[7600] | 152 | if (size == 0 || cs_malloc(&tmp, size)) {
|
---|
[5440] | 153 | cs_writelock(&loghistory_lock);
|
---|
[5076] | 154 | tmp2 = loghist;
|
---|
| 155 | // On shrinking, the log is not copied and the order is reversed
|
---|
| 156 | if(size < cfg.loghistorysize){
|
---|
| 157 | cfg.loghistorysize = size;
|
---|
| 158 | cs_sleepms(20); // Monitor or webif may be currently outputting the loghistory but don't use locking so we sleep a bit...
|
---|
| 159 | loghistptr = tmp;
|
---|
| 160 | loghist = tmp;
|
---|
| 161 | } else {
|
---|
| 162 | if(loghist){
|
---|
| 163 | memcpy(tmp, loghist, cfg.loghistorysize);
|
---|
| 164 | loghistptr = tmp + (loghistptr - loghist);
|
---|
| 165 | } else loghistptr = tmp;
|
---|
| 166 | loghist = tmp;
|
---|
| 167 | cs_sleepms(20); // Monitor or webif may be currently outputting the loghistory but don't use locking so we sleep a bit...
|
---|
[7156] | 168 | cfg.loghistorysize = size;
|
---|
[5076] | 169 | }
|
---|
[5440] | 170 | cs_writeunlock(&loghistory_lock);
|
---|
[7156] | 171 | if(tmp2 != NULL) add_garbage(tmp2);
|
---|
[5076] | 172 | }
|
---|
| 173 | }
|
---|
| 174 | }
|
---|
[6309] | 175 | #endif
|
---|
[5076] | 176 |
|
---|
[7239] | 177 | static time_t log_ts = 0;
|
---|
| 178 |
|
---|
[5667] | 179 | static int32_t get_log_header(int32_t m, char *txt)
|
---|
[8] | 180 | {
|
---|
[5667] | 181 | struct s_client *cl = cur_client();
|
---|
| 182 | struct tm lt;
|
---|
| 183 | int32_t pos;
|
---|
| 184 |
|
---|
[7239] | 185 | time(&log_ts);
|
---|
| 186 | localtime_r(&log_ts, <);
|
---|
[5667] | 187 |
|
---|
[6149] | 188 | pos = snprintf(txt, LOG_BUF_SIZE, "[LOG000]%4d/%02d/%02d %02d:%02d:%02d ", lt.tm_year+1900, lt.tm_mon+1, lt.tm_mday, lt.tm_hour, lt.tm_min, lt.tm_sec);
|
---|
[5667] | 189 |
|
---|
[7239] | 190 | switch (m) {
|
---|
| 191 | case 1: // Add thread id and reader type
|
---|
[5841] | 192 | return pos + snprintf(txt+pos, LOG_BUF_SIZE-pos, "%8X %c ", cl?cl->tid:0, cl?cl->typ:' ');
|
---|
[7239] | 193 | case 0: // Add thread id
|
---|
[5841] | 194 | return pos + snprintf(txt+pos, LOG_BUF_SIZE-pos, "%8X%-3.3s ", cl?cl->tid:0, "");
|
---|
[7239] | 195 | default: // Add empty thread id
|
---|
| 196 | return pos + snprintf(txt+pos, LOG_BUF_SIZE-pos, "%8X%-3.3s ", 0, "");
|
---|
| 197 | }
|
---|
[8] | 198 | }
|
---|
| 199 |
|
---|
[6309] | 200 | static void write_to_log(char *txt, struct s_log *log, int8_t do_flush)
|
---|
[8] | 201 | {
|
---|
[8319] | 202 | (void)log; // Prevent warning when WEBIF, MODULE_MONITOR and CS_ANTICASC are disabled
|
---|
[8] | 203 |
|
---|
[1952] | 204 | #ifdef CS_ANTICASC
|
---|
[7556] | 205 | extern FILE *ac_log;
|
---|
[7411] | 206 | if (!strncmp(txt + log->header_len, "acasc:", 6) && ac_log) {
|
---|
[5728] | 207 | strcat(txt, "\n");
|
---|
[7411] | 208 | fputs(txt + 8, ac_log);
|
---|
| 209 | fflush(ac_log);
|
---|
[5728] | 210 | } else
|
---|
[1952] | 211 | #endif
|
---|
[5728] | 212 | {
|
---|
| 213 | if (cfg.logtosyslog)
|
---|
| 214 | syslog(LOG_INFO, "%s", txt+24);
|
---|
| 215 | strcat(txt, "\n");
|
---|
| 216 | }
|
---|
[6069] | 217 | cs_write_log(txt + 8, do_flush);
|
---|
[8] | 218 |
|
---|
[7156] | 219 | #if defined(WEBIF) || defined(MODULE_MONITOR)
|
---|
[6552] | 220 | if (loghist && exit_oscam != 1) {
|
---|
[6309] | 221 | char *usrtxt = log->cl_text;
|
---|
[5065] | 222 | char *target_ptr = NULL;
|
---|
[5667] | 223 | int32_t target_len = strlen(usrtxt) + (strlen(txt) - 8) + 1;
|
---|
[7156] | 224 |
|
---|
[6062] | 225 | cs_writelock(&loghistory_lock);
|
---|
[7156] | 226 | char *lastpos = loghist + (cfg.loghistorysize) - 1;
|
---|
[7049] | 227 | if(loghist + target_len + 1 >= lastpos){
|
---|
| 228 | strncpy(txt + 39, "Log entry too long!", strlen(txt) - 39); // we can assume that the min loghistorysize is always 1024 so we don't need to check if this new string fits into it!
|
---|
| 229 | target_len = strlen(usrtxt) + (strlen(txt) - 8) + 1;
|
---|
[7156] | 230 | }
|
---|
[5065] | 231 | if (!loghistptr)
|
---|
| 232 | loghistptr = loghist;
|
---|
[7156] | 233 |
|
---|
[7049] | 234 | if (loghistptr + target_len + 1 > lastpos) {
|
---|
[5065] | 235 | *loghistptr='\0';
|
---|
| 236 | loghistptr=loghist + target_len + 1;
|
---|
| 237 | *loghistptr='\0';
|
---|
| 238 | target_ptr=loghist;
|
---|
| 239 | } else {
|
---|
| 240 | target_ptr = loghistptr;
|
---|
| 241 | loghistptr=loghistptr + target_len + 1;
|
---|
| 242 | *loghistptr='\0';
|
---|
| 243 | }
|
---|
[6062] | 244 | cs_writeunlock(&loghistory_lock);
|
---|
[5065] | 245 |
|
---|
[5667] | 246 | snprintf(target_ptr, target_len + 1, "%s\t%s", usrtxt, txt + 8);
|
---|
[5065] | 247 | }
|
---|
[6309] | 248 | #endif
|
---|
[5065] | 249 |
|
---|
[8303] | 250 | #if defined(MODULE_MONITOR)
|
---|
| 251 | char sbuf[16];
|
---|
[3514] | 252 | struct s_client *cl;
|
---|
[5065] | 253 | for (cl=first_client; cl ; cl=cl->next) {
|
---|
[7156] | 254 | if ((cl->typ == 'm') && (cl->monlvl>0) && cl->log) //this variable is only initialized for cl->typ = 'm'
|
---|
[1762] | 255 | {
|
---|
[3572] | 256 | if (cl->monlvl<2) {
|
---|
[6309] | 257 | if (log->cl_typ != 'c' && log->cl_typ != 'm')
|
---|
[3656] | 258 | continue;
|
---|
[6309] | 259 | if (log->cl_usr && cl->account && strcmp(log->cl_usr, cl->account->usr))
|
---|
[1762] | 260 | continue;
|
---|
[3572] | 261 | }
|
---|
[4382] | 262 | snprintf(sbuf, sizeof(sbuf), "%03d", cl->logcounter);
|
---|
[3445] | 263 | cl->logcounter = (cl->logcounter+1) % 1000;
|
---|
[5667] | 264 | memcpy(txt + 4, sbuf, 3);
|
---|
| 265 | monitor_send_idx(cl, txt);
|
---|
[1762] | 266 | }
|
---|
| 267 | }
|
---|
[8303] | 268 | #endif
|
---|
[8] | 269 | }
|
---|
| 270 |
|
---|
[6062] | 271 | static void write_to_log_int(char *txt, int8_t header_len)
|
---|
[5667] | 272 | {
|
---|
[6585] | 273 | #if !defined(WEBIF) && !defined(MODULE_MONITOR)
|
---|
| 274 | if (cfg.disablelog) return;
|
---|
| 275 | #endif
|
---|
[7590] | 276 | char *newtxt = cs_strdup(txt);
|
---|
| 277 | if (!newtxt)
|
---|
| 278 | return;
|
---|
| 279 | struct s_log *log;
|
---|
[7600] | 280 | if (!cs_malloc(&log, sizeof(struct s_log))) {
|
---|
[7590] | 281 | free(newtxt);
|
---|
| 282 | return;
|
---|
| 283 | }
|
---|
| 284 | log->txt = newtxt;
|
---|
[6062] | 285 | log->header_len = header_len;
|
---|
[6069] | 286 | log->direct_log = 0;
|
---|
[6215] | 287 | struct s_client *cl = cur_client();
|
---|
| 288 | log->cl_usr = "";
|
---|
| 289 | if (!cl){
|
---|
| 290 | log->cl_text = "undef";
|
---|
[7156] | 291 | log->cl_typ = ' ';
|
---|
[6215] | 292 | } else {
|
---|
| 293 | switch(cl->typ) {
|
---|
| 294 | case 'c':
|
---|
| 295 | case 'm':
|
---|
| 296 | if(cl->account) {
|
---|
| 297 | log->cl_text = cl->account->usr;
|
---|
| 298 | log->cl_usr = cl->account->usr;
|
---|
| 299 | } else log->cl_text = "";
|
---|
| 300 | break;
|
---|
| 301 | case 'p':
|
---|
| 302 | case 'r':
|
---|
| 303 | log->cl_text = cl->reader ? cl->reader->label : "";
|
---|
| 304 | break;
|
---|
| 305 | default:
|
---|
| 306 | log->cl_text = "server";
|
---|
| 307 | break;
|
---|
| 308 | }
|
---|
| 309 | log->cl_typ = cl->typ;
|
---|
| 310 | }
|
---|
[6585] | 311 |
|
---|
| 312 | if(exit_oscam == 1 || cfg.disablelog){ //Exit or log disabled. if disabled, just display on webif/monitor
|
---|
[6391] | 313 | char buf[LOG_BUF_SIZE];
|
---|
| 314 | cs_strncpy(buf, log->txt, LOG_BUF_SIZE);
|
---|
| 315 | write_to_log(buf, log, 1);
|
---|
[6423] | 316 | free(log->txt);
|
---|
[6391] | 317 | free(log);
|
---|
| 318 | } else
|
---|
[7156] | 319 | ll_append(log_list, log);
|
---|
[6062] | 320 | }
|
---|
| 321 |
|
---|
[7239] | 322 | static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
|
---|
| 323 | static char last_log_txt[LOG_BUF_SIZE] = { 0 };
|
---|
| 324 | static time_t last_log_ts = 0;
|
---|
| 325 | static unsigned int last_log_duplicates = 0;
|
---|
| 326 |
|
---|
[6126] | 327 | void cs_log_int(uint16_t mask, int8_t lock __attribute__((unused)), const uchar *buf, int32_t n, const char *fmt, ...)
|
---|
[6062] | 328 | {
|
---|
[8015] | 329 | if ((mask & cs_dblevel) || !mask ) {
|
---|
| 330 | va_list params;
|
---|
[8] | 331 |
|
---|
[8015] | 332 | static char log_txt[LOG_BUF_SIZE], dupl[LOG_BUF_SIZE/4];
|
---|
| 333 | int32_t dupl_header_len, repeated_line, i, len = 0;
|
---|
| 334 | pthread_mutex_lock(&log_mutex);
|
---|
| 335 | if (fmt)
|
---|
| 336 | {
|
---|
| 337 | va_start(params, fmt);
|
---|
| 338 | len = get_log_header(1, log_txt);
|
---|
| 339 | vsnprintf(log_txt + len, sizeof(log_txt) - len, fmt, params);
|
---|
| 340 | va_end(params);
|
---|
[8285] | 341 | if (cfg.logduplicatelines) {
|
---|
[8015] | 342 | memcpy(last_log_txt, log_txt + len, LOG_BUF_SIZE);
|
---|
| 343 | write_to_log_int(log_txt, len);
|
---|
| 344 | } else {
|
---|
[8285] | 345 | repeated_line = strcmp(last_log_txt, log_txt + len) == 0;
|
---|
| 346 | if (last_log_duplicates > 0) {
|
---|
| 347 | if (!last_log_ts) // Must be initialized once
|
---|
| 348 | last_log_ts = log_ts;
|
---|
| 349 | // Report duplicated lines when the new log line is different
|
---|
| 350 | // than the old or 60 seconds have passed.
|
---|
| 351 | if (!repeated_line || log_ts - last_log_ts >= 60) {
|
---|
| 352 | dupl_header_len = get_log_header(2, dupl);
|
---|
| 353 | snprintf(dupl + dupl_header_len - 1, sizeof(dupl) - dupl_header_len, "--- Skipped %u duplicated log lines ---", last_log_duplicates);
|
---|
| 354 | write_to_log_int(dupl, 0);
|
---|
| 355 | last_log_duplicates = 0;
|
---|
| 356 | last_log_ts = log_ts;
|
---|
| 357 | }
|
---|
| 358 | }
|
---|
| 359 | if (!repeated_line) {
|
---|
| 360 | memcpy(last_log_txt, log_txt + len, LOG_BUF_SIZE);
|
---|
| 361 | write_to_log_int(log_txt, len);
|
---|
| 362 | } else {
|
---|
| 363 | last_log_duplicates++;
|
---|
| 364 | }
|
---|
[8015] | 365 | }
|
---|
[7239] | 366 | }
|
---|
[8015] | 367 | if (buf)
|
---|
[1762] | 368 | {
|
---|
[8015] | 369 | for (i=0; i<n; i+=16)
|
---|
| 370 | {
|
---|
| 371 | len = get_log_header(0, log_txt);
|
---|
| 372 | cs_hexdump(1, buf+i, (n-i>16) ? 16 : n-i, log_txt + len, sizeof(log_txt) - len);
|
---|
| 373 | write_to_log_int(log_txt, len);
|
---|
| 374 | }
|
---|
[1762] | 375 | }
|
---|
[8015] | 376 | pthread_mutex_unlock(&log_mutex);
|
---|
[1762] | 377 | }
|
---|
[1399] | 378 | }
|
---|
[5361] | 379 |
|
---|
[5667] | 380 | void cs_close_log(void)
|
---|
| 381 | {
|
---|
[6202] | 382 | //Wait for log close:
|
---|
[7174] | 383 | int32_t i = 0;
|
---|
[8068] | 384 | while (ll_count(log_list) > 0 && i < 1000) {
|
---|
| 385 | cs_sleepms(1);
|
---|
[7174] | 386 | ++i;
|
---|
[6202] | 387 | }
|
---|
[7174] | 388 | if(fp){
|
---|
| 389 | fclose(fp);
|
---|
| 390 | fp=(FILE *)0;
|
---|
| 391 | }
|
---|
[5667] | 392 | }
|
---|
| 393 |
|
---|
[5361] | 394 | /*
|
---|
| 395 | * This function writes the current CW from ECM struct to a cwl file.
|
---|
| 396 | * The filename is re-calculated and file re-opened every time.
|
---|
| 397 | * This will consume a bit cpu time, but nothing has to be stored between
|
---|
| 398 | * each call. If not file exists, a header is prepended
|
---|
| 399 | */
|
---|
[5739] | 400 | void logCWtoFile(ECM_REQUEST *er, uchar *cw){
|
---|
[5361] | 401 | FILE *pfCWL;
|
---|
| 402 | char srvname[128];
|
---|
| 403 | /* %s / %s _I %04X _ %s .cwl */
|
---|
| 404 | char buf[256 + sizeof(srvname)];
|
---|
[5517] | 405 | char date[9];
|
---|
[5361] | 406 | unsigned char i, parity, writeheader = 0;
|
---|
| 407 | time_t t;
|
---|
| 408 | struct tm timeinfo;
|
---|
| 409 |
|
---|
| 410 | /*
|
---|
| 411 | * search service name for that id and change characters
|
---|
| 412 | * causing problems in file name
|
---|
| 413 | */
|
---|
| 414 |
|
---|
| 415 | get_servicename(cur_client(), er->srvid, er->caid, srvname);
|
---|
| 416 |
|
---|
| 417 | for (i = 0; srvname[i]; i++)
|
---|
| 418 | if (srvname[i] == ' ') srvname[i] = '_';
|
---|
| 419 |
|
---|
| 420 | /* calc log file name */
|
---|
| 421 | time(&t);
|
---|
| 422 | localtime_r(&t, &timeinfo);
|
---|
| 423 | strftime(date, sizeof(date), "%Y%m%d", &timeinfo);
|
---|
| 424 | snprintf(buf, sizeof(buf), "%s/%s_I%04X_%s.cwl", cfg.cwlogdir, date, er->srvid, srvname);
|
---|
| 425 |
|
---|
| 426 | /* open failed, assuming file does not exist, yet */
|
---|
| 427 | if((pfCWL = fopen(buf, "r")) == NULL) {
|
---|
| 428 | writeheader = 1;
|
---|
| 429 | } else {
|
---|
| 430 | /* we need to close the file if it was opened correctly */
|
---|
| 431 | fclose(pfCWL);
|
---|
| 432 | }
|
---|
| 433 |
|
---|
| 434 | if ((pfCWL = fopen(buf, "a+")) == NULL) {
|
---|
| 435 | /* maybe this fails because the subdir does not exist. Is there a common function to create it?
|
---|
| 436 | for the moment do not print32_t to log on every ecm
|
---|
| 437 | cs_log(""error opening cw logfile for writing: %s (errno=%d %s)", buf, errno, strerror(errno)); */
|
---|
| 438 | return;
|
---|
| 439 | }
|
---|
| 440 | if (writeheader) {
|
---|
| 441 | /* no global macro for cardserver name :( */
|
---|
[7502] | 442 | fprintf(pfCWL, "# OSCam cardserver v%s - http://www.streamboard.tv/oscam/\n", CS_VERSION);
|
---|
[5361] | 443 | fprintf(pfCWL, "# control word log file for use with tsdec offline decrypter\n");
|
---|
| 444 | strftime(buf, sizeof(buf),"DATE %Y-%m-%d, TIME %H:%M:%S, TZ %Z\n", &timeinfo);
|
---|
| 445 | fprintf(pfCWL, "# %s", buf);
|
---|
| 446 | fprintf(pfCWL, "# CAID 0x%04X, SID 0x%04X, SERVICE \"%s\"\n", er->caid, er->srvid, srvname);
|
---|
| 447 | }
|
---|
| 448 |
|
---|
| 449 | parity = er->ecm[0]&1;
|
---|
| 450 | fprintf(pfCWL, "%d ", parity);
|
---|
| 451 | for (i = parity * 8; i < 8 + parity * 8; i++)
|
---|
[5739] | 452 | fprintf(pfCWL, "%02X ", cw[i]);
|
---|
[5361] | 453 | /* better use incoming time er->tps rather than current time? */
|
---|
| 454 | strftime(buf,sizeof(buf),"%H:%M:%S\n", &timeinfo);
|
---|
| 455 | fprintf(pfCWL, "# %s", buf);
|
---|
| 456 | fflush(pfCWL);
|
---|
| 457 | fclose(pfCWL);
|
---|
| 458 | }
|
---|
| 459 |
|
---|
[7156] | 460 | int32_t cs_init_statistics(void)
|
---|
[8] | 461 | {
|
---|
[4588] | 462 | if ((!fps) && (cfg.usrfile != NULL))
|
---|
[1762] | 463 | {
|
---|
[4588] | 464 | if ((fps=fopen(cfg.usrfile, "a+"))<=(FILE *)0)
|
---|
[1762] | 465 | {
|
---|
| 466 | fps=(FILE *)0;
|
---|
[4588] | 467 | cs_log("couldn't open statistics file: %s", cfg.usrfile);
|
---|
[1762] | 468 | }
|
---|
| 469 | }
|
---|
| 470 | return(fps<=(FILE *)0);
|
---|
[8] | 471 | }
|
---|
| 472 |
|
---|
[3355] | 473 | void cs_statistics(struct s_client * client)
|
---|
[8] | 474 | {
|
---|
[4588] | 475 | if (!cfg.disableuserfile){
|
---|
[1901] | 476 | time_t t;
|
---|
[4356] | 477 | struct tm lt;
|
---|
[4195] | 478 | char buf[LOG_BUF_SIZE];
|
---|
[8] | 479 |
|
---|
[1725] | 480 | float cwps;
|
---|
[8] | 481 |
|
---|
[1725] | 482 | time(&t);
|
---|
[4356] | 483 | localtime_r(&t, <);
|
---|
[3355] | 484 | if (client->cwfound+client->cwnot>0)
|
---|
[1725] | 485 | {
|
---|
[3355] | 486 | cwps=client->last-client->login;
|
---|
| 487 | cwps/=client->cwfound+client->cwnot;
|
---|
[1725] | 488 | }
|
---|
| 489 | else
|
---|
| 490 | cwps=0;
|
---|
[1764] | 491 |
|
---|
[5309] | 492 | char channame[32];
|
---|
[7409] | 493 | if (cfg.appendchaninfo)
|
---|
[5309] | 494 | get_servicename(client, client->last_srvid,client->last_caid, channame);
|
---|
| 495 | else
|
---|
| 496 | channame[0] = '\0';
|
---|
[1764] | 497 |
|
---|
[4994] | 498 | int32_t lsec;
|
---|
[3355] | 499 | if ((client->last_caid == 0xFFFF) && (client->last_srvid == 0xFFFF))
|
---|
| 500 | lsec = client->last - client->login; //client leave calc total duration
|
---|
[1906] | 501 | else
|
---|
[3355] | 502 | lsec = client->last - client->lastswitch;
|
---|
[1906] | 503 |
|
---|
[4994] | 504 | int32_t secs = 0, fullmins = 0, mins = 0, fullhours = 0;
|
---|
[1903] | 505 |
|
---|
| 506 | if((lsec > 0) && (lsec < 1000000)) {
|
---|
| 507 | secs = lsec % 60;
|
---|
| 508 | if (lsec > 60) {
|
---|
| 509 | fullmins = lsec / 60;
|
---|
| 510 | mins = fullmins % 60;
|
---|
| 511 | if(fullmins > 60) {
|
---|
| 512 | fullhours = fullmins / 60;
|
---|
| 513 | }
|
---|
| 514 | }
|
---|
| 515 | }
|
---|
| 516 |
|
---|
[1901] | 517 | /* statistics entry start with 's' to filter it out on other end of pipe
|
---|
| 518 | * so we can use the same Pipe as Log
|
---|
| 519 | */
|
---|
[4382] | 520 | snprintf(buf, sizeof(buf), "s%02d.%02d.%02d %02d:%02d:%02d %3.1f %s %s %d %d %d %d %d %d %d %ld %ld %02d:%02d:%02d %s %04X:%04X %s\n",
|
---|
[4356] | 521 | lt.tm_mday, lt.tm_mon+1, lt.tm_year%100,
|
---|
| 522 | lt.tm_hour, lt.tm_min, lt.tm_sec, cwps,
|
---|
[4245] | 523 | client->account->usr,
|
---|
[3355] | 524 | cs_inet_ntoa(client->ip),
|
---|
| 525 | client->port,
|
---|
| 526 | client->cwfound,
|
---|
| 527 | client->cwcache,
|
---|
| 528 | client->cwnot,
|
---|
| 529 | client->cwignored,
|
---|
| 530 | client->cwtout,
|
---|
| 531 | client->cwtun,
|
---|
| 532 | client->login,
|
---|
| 533 | client->last,
|
---|
[1903] | 534 | fullhours, mins, secs,
|
---|
[8360] | 535 | get_module(client)->desc,
|
---|
[3355] | 536 | client->last_caid,
|
---|
| 537 | client->last_srvid,
|
---|
[5309] | 538 | channame);
|
---|
[1764] | 539 |
|
---|
[6069] | 540 | cs_write_log_int(buf);
|
---|
[1725] | 541 | }
|
---|
[8] | 542 | }
|
---|
[6062] | 543 |
|
---|
[6900] | 544 | void log_list_thread(void)
|
---|
[6062] | 545 | {
|
---|
| 546 | char buf[LOG_BUF_SIZE];
|
---|
[8282] | 547 | set_thread_name(__func__);
|
---|
[6585] | 548 | int last_count=ll_count(log_list), count, grow_count=0, write_count;
|
---|
[7107] | 549 | do {
|
---|
[6062] | 550 | LL_ITER it = ll_iter_create(log_list);
|
---|
| 551 | struct s_log *log;
|
---|
[6585] | 552 | write_count = 0;
|
---|
[6069] | 553 | while ((log=ll_iter_next_remove(&it))) {
|
---|
| 554 | int8_t do_flush = ll_count(log_list) == 0; //flush on writing last element
|
---|
[6062] | 555 |
|
---|
| 556 | cs_strncpy(buf, log->txt, LOG_BUF_SIZE);
|
---|
[6069] | 557 | if (log->direct_log)
|
---|
| 558 | cs_write_log(buf, do_flush);
|
---|
| 559 | else
|
---|
[6309] | 560 | write_to_log(buf, log, do_flush);
|
---|
[6062] | 561 | free(log->txt);
|
---|
| 562 | free(log);
|
---|
[6585] | 563 |
|
---|
| 564 | //If list is faster growing than we could write to file, drop list:
|
---|
| 565 | write_count++;
|
---|
| 566 | if (write_count%10000 == 0) { //check every 10000 writes:
|
---|
| 567 | count = ll_count(log_list);
|
---|
| 568 | if (count > last_count) {
|
---|
| 569 | grow_count++;
|
---|
| 570 | if (grow_count > 5) { //5 times still growing
|
---|
| 571 | cs_write_log("------------->logging temporary disabled (30s) - too much data!\n", 1);
|
---|
| 572 | cfg.disablelog = 1;
|
---|
| 573 | ll_iter_reset(&it);
|
---|
| 574 | while ((log=ll_iter_next_remove(&it))) { //clear log
|
---|
| 575 | free(log->txt);
|
---|
| 576 | free(log);
|
---|
| 577 | }
|
---|
| 578 | cs_sleepms(30*1000);
|
---|
| 579 | cfg.disablelog = 0;
|
---|
| 580 |
|
---|
| 581 | grow_count = 0;
|
---|
| 582 | last_count = 0;
|
---|
| 583 | break;
|
---|
| 584 | }
|
---|
| 585 | }
|
---|
| 586 | else
|
---|
| 587 | grow_count = 0;
|
---|
| 588 | last_count = count;
|
---|
| 589 | }
|
---|
[6062] | 590 | }
|
---|
[6578] | 591 | cs_sleepms(250);
|
---|
[7174] | 592 | } while(1);
|
---|
[6062] | 593 | }
|
---|
| 594 |
|
---|
| 595 | int32_t cs_init_log(void)
|
---|
| 596 | {
|
---|
| 597 | if(logStarted == 0){
|
---|
[7156] | 598 | #if defined(WEBIF) || defined(MODULE_MONITOR)
|
---|
[6062] | 599 | cs_lock_create(&loghistory_lock, 5, "loghistory_lock");
|
---|
[6309] | 600 | #endif
|
---|
[6062] | 601 |
|
---|
[6069] | 602 | log_list = ll_create(LOG_LIST);
|
---|
[6062] | 603 | start_thread((void*)&log_list_thread, "log_list_thread");
|
---|
| 604 | }
|
---|
[7174] | 605 | int32_t rc = 0;
|
---|
| 606 | if(!cfg.disablelog) rc = cs_open_logfiles();
|
---|
[6062] | 607 | logStarted = 1;
|
---|
| 608 | return rc;
|
---|
| 609 | }
|
---|
[6578] | 610 |
|
---|
| 611 | void cs_disable_log(int8_t disabled)
|
---|
| 612 | {
|
---|
| 613 | if (cfg.disablelog != disabled) {
|
---|
[7174] | 614 | if(disabled && logStarted) {
|
---|
| 615 | cs_log("Stopping log...");
|
---|
| 616 | int32_t i = 0;
|
---|
| 617 | while(ll_count(log_list) > 0 && i < 200){
|
---|
| 618 | cs_sleepms(5);
|
---|
| 619 | ++i;
|
---|
| 620 | }
|
---|
| 621 | }
|
---|
[6578] | 622 | cfg.disablelog = disabled;
|
---|
[7174] | 623 | if (disabled){
|
---|
| 624 | if(logStarted) {
|
---|
| 625 | cs_sleepms(20);
|
---|
[6578] | 626 | cs_close_log();
|
---|
| 627 | }
|
---|
[7107] | 628 | } else {
|
---|
[7174] | 629 | cs_open_logfiles();
|
---|
[6578] | 630 | }
|
---|
| 631 | }
|
---|
| 632 | }
|
---|
| 633 |
|
---|