3 #include "module-anticasc.h"
4 #include "module-monitor.h"
5 #include "oscam-client.h"
6 #include "oscam-garbage.h"
7 #include "oscam-lock.h"
10 #include "oscam-string.h"
11 #include "oscam-time.h"
13 // Do not allow log_list to grow bigger than that many entries
14 #define MAX_LOG_LIST_BACKLOG 10000
16 extern char *syslog_ident
;
17 extern int32_t exit_oscam
;
19 char *LOG_LIST
= "log_list";
20 int8_t logStarted
= 0;
24 static LLIST
*log_list
;
25 static bool log_running
;
26 static int log_list_queued
;
27 static pthread_t log_thread
;
28 static pthread_cond_t log_thread_sleep_cond
;
29 static pthread_mutex_t log_thread_sleep_cond_mutex
;
30 static int32_t syslog_socket
= -1;
31 static struct sockaddr_in syslog_addr
;
38 uint8_t header_logcount_offset
;
39 uint8_t header_date_offset
;
40 uint8_t header_time_offset
;
41 uint8_t header_info_offset
;
48 #define LOG_BUF_SIZE 512
50 static void switch_log(char *file
, FILE **f
, int32_t (*pfinit
)(void))
52 if(cfg
.max_log_size
&& file
) //only 1 thread needs to switch the log; even if anticasc, statistics and normal log are running
53 //at the same time, it is ok to have the other logs switching 1 entry later
55 if(*f
!= NULL
&& ftell(*f
) >= cfg
.max_log_size
* 1024)
58 char prev_log
[strlen(file
) + 6];
59 snprintf(prev_log
, sizeof(prev_log
), "%s-prev", file
);
60 fprintf(*f
, "switch log file\n");
64 rc
= rename(file
, prev_log
);
67 fprintf(stderr
, "rename(%s, %s) failed (errno=%d %s)\n", file
, prev_log
, errno
, strerror(errno
));
71 fprintf(stderr
, "Initialisation of log file failed, continuing without logging thread %8lX. Log will be output to stdout!", (unsigned long)pthread_self());
78 void cs_reopen_log(void)
84 fprintf(fp
, "flush and re-open log file\n");
89 if(cs_open_logfiles())
91 fprintf(stderr
, "Initialisation of log file failed, continuing without logging thread %8luX. Log will be output to stdout!", (unsigned long)pthread_self());
99 fprintf(fps
, "flush and re-open user log file\n");
104 if(cs_init_statistics())
106 fprintf(stderr
, "Initialisation of user log file failed, continuing without logging thread %8luX.", (unsigned long)pthread_self());
111 static void cs_write_log(char *txt
, int8_t do_flush
, uint8_t hdr_date_offset
, uint8_t hdr_time_offset
)
113 // filter out entries with leading 's' and forward to statistics
114 if(txt
[hdr_date_offset
] == 's')
118 switch_log(cfg
.usrfile
, &fps
, cs_init_statistics
);
121 fputs(txt
+ hdr_date_offset
+ 1, fps
); // remove the leading 's' and write to file
122 if(do_flush
) { fflush(fps
); }
132 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
135 fputs(txt
+ hdr_date_offset
, fp
);
136 if(do_flush
) { fflush(fp
); }
141 fputs(txt
+ hdr_time_offset
, stdout
);
142 if(do_flush
) { fflush(stdout
); }
148 static void log_list_flush(void)
153 SAFE_COND_SIGNAL_NOLOG(&log_thread_sleep_cond
);
155 while(ll_count(log_list
) > 0 && i
< 200)
162 static void log_list_add(struct s_log
*log
)
167 int32_t count
= ll_count(log_list
);
169 if(count
< MAX_LOG_LIST_BACKLOG
)
171 ll_append(log_list
, log
);
173 else // We have too much backlog
177 cs_write_log("-------------> Too much data in log_list, dropping log message.\n", 1, 0, 0);
179 SAFE_COND_SIGNAL_NOLOG(&log_thread_sleep_cond
);
182 static void cs_write_log_int(char *txt
)
186 cs_write_log(txt
, 1, 0, 0);
190 char *newtxt
= cs_strdup(txt
);
194 if(!cs_malloc(&log
, sizeof(struct s_log
)))
206 int32_t cs_open_logfiles(void)
209 if(logStarted
) { starttext
= "log switched"; }
210 else { starttext
= "started"; }
211 if(!fp
&& cfg
.logfile
) //log to file
213 if((fp
= fopen(cfg
.logfile
, "a+")) <= (FILE *)0)
216 fprintf(stderr
, "couldn't open logfile: %s (errno %d %s)\n", cfg
.logfile
, errno
, strerror(errno
));
221 memset(line
, '-', sizeof(line
));
222 line
[(sizeof(line
) / sizeof(char)) - 1] = '\0';
223 time_t walltime
= cs_time();
227 cs_ctime_r(&walltime
, buf
);
228 fprintf(fp
, "\n%s\n>> OSCam << cardserver %s at %s%s\n", line
, starttext
, buf
, line
);
232 // according to syslog docu: calling closelog is not necessary and calling openlog multiple times is safe
233 // We use openlog to set the default syslog settings so that it's possible to allow switching syslog on and off
234 openlog(syslog_ident
, LOG_NDELAY
| LOG_PID
, LOG_DAEMON
);
235 cs_log(">> OSCam << cardserver %s, version " CS_VERSION
", build r" CS_SVN_VERSION
" (" CS_TARGET
")", starttext
);
237 return (fp
<= (FILE *)0);
240 #if defined(WEBIF) || defined(MODULE_MONITOR)
242 static uint64_t counter
= 0;
243 LLIST
*log_history
= NULL
;
246 This function allows to reinit the in-memory loghistory with a new size.
248 void cs_reinit_loghist(uint32_t size
)
250 if(cfg
.loghistorylines
!= size
)
252 cfg
.loghistorylines
= size
;
257 static struct timeb log_ts
;
259 static uint8_t get_log_header(char *txt
, int32_t txt_size
, uint8_t* hdr_logcount_offset
,
260 uint8_t* hdr_date_offset
, uint8_t* hdr_time_offset
, uint8_t* hdr_info_offset
)
262 struct s_client
*cl
= cur_client();
267 time_t walltime
= cs_walltime(&log_ts
);
268 localtime_r(&walltime
, <
);
270 tmp
= snprintf(txt
, txt_size
, "[LOG000]%04d/%02d/%02d %02d:%02d:%02d %08X %c ",
283 if(hdr_logcount_offset
!= NULL
)
285 // depends on snprintf(...) format
286 (*hdr_logcount_offset
) = 4;
288 if(hdr_date_offset
!= NULL
)
290 // depends on snprintf(...) format
291 (*hdr_date_offset
) = *hdr_logcount_offset
+ 4;
293 if(hdr_time_offset
!= NULL
)
295 // depends on snprintf(...) format
296 (*hdr_time_offset
) = *hdr_date_offset
+ 11;
298 if(hdr_info_offset
!= NULL
)
300 // depends on snprintf(...) format
301 (*hdr_info_offset
) = *hdr_time_offset
+ 9;
307 if(hdr_logcount_offset
!= NULL
)
309 (*hdr_logcount_offset
) = 0;
311 if(hdr_date_offset
!= NULL
)
313 (*hdr_date_offset
) = 0;
315 if(hdr_time_offset
!= NULL
)
317 (*hdr_time_offset
) = 0;
319 if(hdr_info_offset
!= NULL
)
321 (*hdr_info_offset
) = 0;
327 static void write_to_log(char *txt
, struct s_log
*log
, int8_t do_flush
)
332 (void)log
; // Prevent warning when WEBIF, MODULE_MONITOR and CS_ANTICASC are disabled
334 // anticascading messages go to their own log
335 if (!anticasc_logging(txt
+ log
->header_date_offset
))
339 syslog(LOG_INFO
, "%s", txt
+ log
->header_info_offset
);
342 if (cfg
.sysloghost
!= NULL
&& syslog_socket
!= -1)
344 char tmp
[128+LOG_BUF_SIZE
];
345 static char hostname
[64];
346 static uint8_t have_hostname
= 0;
353 if(gethostname(hostname
, 64) != 0)
355 cs_strncpy(hostname
, "unknown", 64);
361 walltime
= cs_time();
362 localtime_r(&walltime
, <
);
364 if(strftime(timebuf
, 32, "%b %d %H:%M:%S", <
) == 0)
366 cs_strncpy(timebuf
, "unknown", 32);
369 snprintf(tmp
, sizeof(tmp
), "%s %s oscam[%u]: %s", timebuf
, hostname
, getpid(), txt
+ log
->header_info_offset
);
370 sendto(syslog_socket
, tmp
, strlen(tmp
), 0, (struct sockaddr
*) &syslog_addr
, sizeof(syslog_addr
));
375 cs_write_log(txt
, do_flush
, log
->header_date_offset
, log
->header_time_offset
);
377 #if defined(WEBIF) || defined(MODULE_MONITOR)
378 if(!exit_oscam
&& cfg
.loghistorylines
&& log_history
)
380 struct s_log_history
*hist
;
382 while((uint32_t)ll_count(log_history
) >= cfg
.loghistorylines
)
384 hist
= ll_remove_first(log_history
);
387 add_garbage(hist
->txt
);
393 if(cs_malloc(&hist
, sizeof(struct s_log_history
)))
395 int32_t target_len
= strlen(log
->cl_text
) + strlen(txt
+log
->header_date_offset
) + 1;
397 if(cs_malloc(&hist
->txt
, sizeof(char) * (target_len
+ 1)))
399 hist
->counter
= counter
++;
400 snprintf(hist
->txt
, target_len
+ 1, "%s\t%s", log
->cl_text
, txt
+ log
->header_date_offset
);
402 ll_append(log_history
, hist
);
412 #if defined(MODULE_MONITOR)
415 for(cl
= first_client
; cl
; cl
= cl
->next
)
417 if((cl
->typ
== 'm') && (cl
->monlvl
> 0) && cl
->log
) //this variable is only initialized for cl->typ = 'm'
421 if(log
->cl_typ
!= 'c' && log
->cl_typ
!= 'm')
423 if(log
->cl_usr
&& cl
->account
&& strcmp(log
->cl_usr
, cl
->account
->usr
))
427 if(log
->header_len
> 0)
429 snprintf(sbuf
, sizeof(sbuf
), "%03d", cl
->logcounter
);
430 cl
->logcounter
= (cl
->logcounter
+ 1) % 1000;
431 memcpy(txt
+ log
->header_logcount_offset
, sbuf
, 3);
432 monitor_send_idx(cl
, txt
);
436 char tmp_log
[8+LOG_BUF_SIZE
];
437 snprintf(tmp_log
, sizeof(tmp_log
), "[LOG%03d]%s", cl
->logcounter
, txt
);
438 cl
->logcounter
= (cl
->logcounter
+ 1) % 1000;
439 monitor_send_idx(cl
, tmp_log
);
446 static void write_to_log_int(char *txt
, uint8_t header_len
, uint8_t hdr_logcount_offset
, uint8_t hdr_date_offset
, uint8_t hdr_time_offset
, uint8_t hdr_info_offset
)
448 #if !defined(WEBIF) && !defined(MODULE_MONITOR)
449 if(cfg
.disablelog
) { return; }
451 char *newtxt
= cs_strdup(txt
);
455 if(!cs_malloc(&log
, sizeof(struct s_log
)))
461 log
->header_len
= header_len
;
462 log
->header_logcount_offset
= hdr_logcount_offset
;
463 log
->header_date_offset
= hdr_date_offset
;
464 log
->header_time_offset
= hdr_time_offset
;
465 log
->header_info_offset
= hdr_info_offset
;
467 struct s_client
*cl
= cur_client();
471 log
->cl_text
= "undef";
482 log
->cl_text
= cl
->account
->usr
;
483 log
->cl_usr
= cl
->account
->usr
;
485 else { log
->cl_text
= ""; }
489 log
->cl_text
= cl
->reader
? cl
->reader
->label
: "";
492 log
->cl_text
= "server";
495 log
->cl_typ
= cl
->typ
;
498 if(exit_oscam
== 1 || cfg
.disablelog
) //Exit or log disabled. if disabled, just display on webif/monitor
500 char buf
[LOG_BUF_SIZE
];
501 cs_strncpy(buf
, log
->txt
, LOG_BUF_SIZE
);
502 write_to_log(buf
, log
, 1);
507 { log_list_add(log
); }
510 static pthread_mutex_t log_mutex
;
511 static char log_txt
[LOG_BUF_SIZE
];
512 static char dupl
[LOG_BUF_SIZE
/ 4];
513 static char last_log_txt
[LOG_BUF_SIZE
];
514 static struct timeb last_log_ts
;
515 static unsigned int last_log_duplicates
;
517 static void __cs_log_check_duplicates(uint8_t hdr_len
, uint8_t hdr_logcount_offset
, uint8_t hdr_date_offset
, uint8_t hdr_time_offset
, uint8_t hdr_info_offset
)
519 bool repeated_line
= strcmp(last_log_txt
, log_txt
+ hdr_len
) == 0;
520 if (last_log_duplicates
> 0)
522 if (!cs_valid_time(&last_log_ts
)) // Must be initialized once
523 last_log_ts
= log_ts
;
524 // Report duplicated lines when the new log line is different
525 // than the old or 60 seconds have passed.
526 int64_t gone
= comp_timeb(&log_ts
, &last_log_ts
);
527 if (!repeated_line
|| gone
>= 60*1000)
529 uint8_t dupl_hdr_logcount_offset
= 0, dupl_hdr_date_offset
= 0, dupl_hdr_time_offset
= 0, dupl_hdr_info_offset
= 0;
530 uint8_t dupl_header_len
= get_log_header(dupl
, sizeof(dupl
), &dupl_hdr_logcount_offset
, &dupl_hdr_date_offset
, &dupl_hdr_time_offset
, &dupl_hdr_info_offset
);
531 snprintf(dupl
+ dupl_header_len
- 1, sizeof(dupl
) - dupl_header_len
, " (-) -- Skipped %u duplicated log lines --", last_log_duplicates
);
532 write_to_log_int(dupl
, dupl_header_len
, dupl_hdr_logcount_offset
, dupl_hdr_date_offset
, dupl_hdr_time_offset
, dupl_hdr_info_offset
);
533 last_log_duplicates
= 0;
534 last_log_ts
= log_ts
;
539 memcpy(last_log_txt
, log_txt
+ hdr_len
, LOG_BUF_SIZE
- hdr_len
);
540 write_to_log_int(log_txt
, hdr_len
, hdr_logcount_offset
, hdr_date_offset
, hdr_time_offset
, hdr_info_offset
);
542 last_log_duplicates
++;
546 #define __init_log_prefix(fmt) \
547 uint8_t hdr_logcount_offset = 0, hdr_date_offset = 0, hdr_time_offset = 0, hdr_info_offset = 0; \
548 uint8_t hdr_len = get_log_header(log_txt, sizeof(log_txt), &hdr_logcount_offset, &hdr_date_offset, &hdr_time_offset, &hdr_info_offset); \
549 int32_t log_prefix_len = 0; \
553 snprintf(_lp, sizeof(_lp), "(%s)", log_prefix); \
554 log_prefix_len = snprintf(log_txt + hdr_len, sizeof(log_txt) - hdr_len, fmt, _lp); \
561 va_start(params, fmt); \
562 __init_log_prefix("%10s "); \
563 vsnprintf(log_txt + hdr_len + log_prefix_len, sizeof(log_txt) - (hdr_len + log_prefix_len), fmt, params); \
565 if (cfg.logduplicatelines) \
567 memcpy(last_log_txt, log_txt + hdr_len, LOG_BUF_SIZE - hdr_len); \
568 write_to_log_int(log_txt, hdr_len, hdr_logcount_offset, hdr_date_offset, hdr_time_offset, hdr_info_offset); \
570 __cs_log_check_duplicates(hdr_len, hdr_logcount_offset, hdr_date_offset, hdr_time_offset, hdr_info_offset); \
574 void cs_log_txt(const char *log_prefix
, const char *fmt
, ...)
579 SAFE_MUTEX_LOCK_NOLOG(&log_mutex
);
581 SAFE_MUTEX_UNLOCK_NOLOG(&log_mutex
);
584 void cs_log_hex(const char *log_prefix
, const uint8_t *buf
, int32_t n
, const char *fmt
, ...)
589 SAFE_MUTEX_LOCK_NOLOG(&log_mutex
);
594 __init_log_prefix("%10s ");
595 for(i
= 0; i
< n
; i
+= 16)
597 cs_hexdump(1, buf
+ i
, (n
- i
> 16) ? 16 : n
- i
, log_txt
+ hdr_len
+ log_prefix_len
, sizeof(log_txt
) - (hdr_len
+ log_prefix_len
));
598 write_to_log_int(log_txt
, hdr_len
, hdr_logcount_offset
, hdr_date_offset
, hdr_time_offset
, hdr_info_offset
);
601 SAFE_MUTEX_UNLOCK_NOLOG(&log_mutex
);
604 static void cs_close_log(void)
614 int32_t cs_init_statistics(void)
616 if((!fps
) && (cfg
.usrfile
!= NULL
))
618 if((fps
= fopen(cfg
.usrfile
, "a+")) <= (FILE *)0)
621 cs_log("couldn't open statistics file: %s", cfg
.usrfile
);
624 return (fps
<= (FILE *)0);
627 void cs_statistics(struct s_client
*client
)
629 if(!cfg
.disableuserfile
)
632 char buf
[LOG_BUF_SIZE
];
636 time_t walltime
= cs_time();
637 localtime_r(&walltime
, <
);
638 if(client
->cwfound
+ client
->cwnot
> 0)
640 cwps
= client
->last
- client
->login
;
641 cwps
/= client
->cwfound
+ client
->cwnot
;
646 char channame
[CS_SERVICENAME_SIZE
];
647 get_servicename(client
, client
->last_srvid
, client
->last_provid
, client
->last_caid
, channame
, sizeof(channame
));
650 if((client
->last_caid
== NO_CAID_VALUE
) && (client
->last_srvid
== NO_SRVID_VALUE
))
651 { lsec
= client
->last
- client
->login
; } //client leave calc total duration
653 { lsec
= client
->last
- client
->lastswitch
; }
655 int32_t secs
= 0, fullmins
= 0, mins
= 0, fullhours
= 0;
657 if((lsec
> 0) && (lsec
< 1000000))
662 fullmins
= lsec
/ 60;
663 mins
= fullmins
% 60;
666 fullhours
= fullmins
/ 60;
671 /* statistics entry start with 's' to filter it out on other end of pipe
672 * so we can use the same Pipe as Log
674 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@%06X:%04X %s\n",
675 lt
.tm_mday
, lt
.tm_mon
+ 1, lt
.tm_year
% 100,
676 lt
.tm_hour
, lt
.tm_min
, lt
.tm_sec
, cwps
,
677 client
->account
->usr
,
678 cs_inet_ntoa(client
->ip
),
688 fullhours
, mins
, secs
,
689 get_module(client
)->desc
,
695 cs_write_log_int(buf
);
699 void log_list_thread(void)
701 char buf
[LOG_BUF_SIZE
];
703 set_thread_name(__func__
);
707 LL_ITER it
= ll_iter_create(log_list
);
709 while((log
= ll_iter_next_remove(&it
)))
711 int8_t do_flush
= ll_count(log_list
) == 0; //flush on writing last element
713 cs_strncpy(buf
, log
->txt
, LOG_BUF_SIZE
);
715 { cs_write_log(buf
, do_flush
, log
->header_date_offset
, log
->header_time_offset
); }
717 { write_to_log(buf
, log
, do_flush
); }
721 if(!log_list_queued
) // The list is empty, sleep until new data comes in and we are woken up
722 sleepms_on_cond(__func__
, &log_thread_sleep_cond_mutex
, &log_thread_sleep_cond
, 60 * 1000);
725 ll_destroy(&log_list
);
728 static void init_syslog_socket(void)
730 if(cfg
.sysloghost
!= NULL
&& syslog_socket
== -1)
734 if ((syslog_socket
= socket(AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
)) == -1)
736 perror("Socket create error!");
739 memset((char *) &syslog_addr
, 0, sizeof(syslog_addr
));
740 syslog_addr
.sin_family
= AF_INET
;
741 syslog_addr
.sin_port
= htons(cfg
.syslogport
);
742 cs_resolve(cfg
.sysloghost
, &in_addr
, NULL
, NULL
);
743 SIN_GET_ADDR(syslog_addr
) = in_addr
;
747 int32_t cs_init_log(void)
751 init_syslog_socket();
752 SAFE_MUTEX_INIT_NOLOG(&log_mutex
, NULL
);
754 cs_pthread_cond_init_nolog(__func__
, &log_thread_sleep_cond_mutex
, &log_thread_sleep_cond
);
756 #if defined(WEBIF) || defined(MODULE_MONITOR)
757 log_history
= ll_create("log history");
760 log_list
= ll_create(LOG_LIST
);
762 int32_t ret
= start_thread_nolog("logging", (void *)&log_list_thread
, NULL
, &log_thread
, 0, 1);
771 if(!cfg
.disablelog
) { rc
= cs_open_logfiles(); }
774 if(cfg
.initial_debuglevel
> 0)
776 cs_dblevel
= cfg
.initial_debuglevel
;
777 cs_log("debug_level=%d", cs_dblevel
);
783 void cs_disable_log(int8_t disabled
)
785 if(cfg
.disablelog
!= disabled
)
787 if(disabled
&& logStarted
)
789 cs_log("Stopping log...");
792 cfg
.disablelog
= disabled
;
797 if(syslog_socket
!= -1)
799 close(syslog_socket
);
809 init_syslog_socket();
817 if(syslog_socket
!= -1)
819 close(syslog_socket
);
824 SAFE_COND_SIGNAL_NOLOG(&log_thread_sleep_cond
);
825 SAFE_THREAD_JOIN_NOLOG(log_thread
, NULL
);