1 | #include "globals.h"
|
---|
2 | #include <syslog.h>
|
---|
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"
|
---|
8 | #include "oscam-log.h"
|
---|
9 | #include "oscam-net.h"
|
---|
10 | #include "oscam-string.h"
|
---|
11 | #include "oscam-time.h"
|
---|
12 |
|
---|
13 | // Do not allow log_list to grow bigger than that many entries
|
---|
14 | #define MAX_LOG_LIST_BACKLOG 10000
|
---|
15 |
|
---|
16 | extern char *syslog_ident;
|
---|
17 | extern int32_t exit_oscam;
|
---|
18 |
|
---|
19 | char *LOG_LIST = "log_list";
|
---|
20 |
|
---|
21 | static FILE *fp;
|
---|
22 | static FILE *fps;
|
---|
23 | static int8_t logStarted;
|
---|
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 |
|
---|
31 | struct s_log
|
---|
32 | {
|
---|
33 | char *txt;
|
---|
34 | int8_t header_len;
|
---|
35 | int8_t direct_log;
|
---|
36 | int8_t cl_typ;
|
---|
37 | char *cl_usr;
|
---|
38 | char *cl_text;
|
---|
39 | };
|
---|
40 |
|
---|
41 | #define LOG_BUF_SIZE 512
|
---|
42 |
|
---|
43 | static void switch_log(char *file, FILE **f, int32_t (*pfinit)(void))
|
---|
44 | {
|
---|
45 | if(cfg.max_log_size && file) //only 1 thread needs to switch the log; even if anticasc, statistics and normal log are running
|
---|
46 | //at the same time, it is ok to have the other logs switching 1 entry later
|
---|
47 | {
|
---|
48 | if(*f != NULL && ftell(*f) >= cfg.max_log_size * 1024)
|
---|
49 | {
|
---|
50 | int32_t rc;
|
---|
51 | char prev_log[strlen(file) + 6];
|
---|
52 | snprintf(prev_log, sizeof(prev_log), "%s-prev", file);
|
---|
53 | fprintf(*f, "switch log file\n");
|
---|
54 | fflush(*f);
|
---|
55 | fclose(*f);
|
---|
56 | *f = (FILE *)0;
|
---|
57 | rc = rename(file, prev_log);
|
---|
58 | if(rc != 0)
|
---|
59 | {
|
---|
60 | fprintf(stderr, "rename(%s, %s) failed (errno=%d %s)\n", file, prev_log, errno, strerror(errno));
|
---|
61 | }
|
---|
62 | else if(pfinit())
|
---|
63 | {
|
---|
64 | fprintf(stderr, "Initialisation of log file failed, continuing without logging thread %8lX. Log will be output to stdout!", (unsigned long)pthread_self());
|
---|
65 | cfg.logtostdout = 1;
|
---|
66 | }
|
---|
67 | }
|
---|
68 | }
|
---|
69 | }
|
---|
70 |
|
---|
71 | void cs_reopen_log(void)
|
---|
72 | {
|
---|
73 | if(cfg.logfile)
|
---|
74 | {
|
---|
75 | if(fp)
|
---|
76 | {
|
---|
77 | fprintf(fp, "flush and re-open log file\n");
|
---|
78 | fflush(fp);
|
---|
79 | fclose(fp);
|
---|
80 | fp = NULL;
|
---|
81 | }
|
---|
82 | if(cs_open_logfiles())
|
---|
83 | {
|
---|
84 | fprintf(stderr, "Initialisation of log file failed, continuing without logging thread %8luX. Log will be output to stdout!", (unsigned long)pthread_self());
|
---|
85 | cfg.logtostdout = 1;
|
---|
86 | }
|
---|
87 | }
|
---|
88 | if(cfg.usrfile)
|
---|
89 | {
|
---|
90 | if(fps)
|
---|
91 | {
|
---|
92 | fprintf(fps, "flush and re-open user log file\n");
|
---|
93 | fflush(fps);
|
---|
94 | fclose(fps);
|
---|
95 | fps = NULL;
|
---|
96 | }
|
---|
97 | if(cs_init_statistics())
|
---|
98 | {
|
---|
99 | fprintf(stderr, "Initialisation of user log file failed, continuing without logging thread %8luX.", (unsigned long)pthread_self());
|
---|
100 | }
|
---|
101 | }
|
---|
102 | }
|
---|
103 |
|
---|
104 | static void cs_write_log(char *txt, int8_t do_flush)
|
---|
105 | {
|
---|
106 | // filter out entries with leading 's' and forward to statistics
|
---|
107 | if(txt[0] == 's')
|
---|
108 | {
|
---|
109 | if(fps)
|
---|
110 | {
|
---|
111 | switch_log(cfg.usrfile, &fps, cs_init_statistics);
|
---|
112 | if(fps)
|
---|
113 | {
|
---|
114 | fputs(txt + 1, fps); // remove the leading 's' and write to file
|
---|
115 | if(do_flush) { fflush(fps); }
|
---|
116 | }
|
---|
117 | }
|
---|
118 | }
|
---|
119 | else
|
---|
120 | {
|
---|
121 | if(!cfg.disablelog)
|
---|
122 | {
|
---|
123 | if(fp)
|
---|
124 | {
|
---|
125 | 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
|
---|
126 | if(fp)
|
---|
127 | {
|
---|
128 | fputs(txt, fp);
|
---|
129 | if(do_flush) { fflush(fp); }
|
---|
130 | }
|
---|
131 | }
|
---|
132 | if(cfg.logtostdout)
|
---|
133 | {
|
---|
134 | fputs(txt + 11, stdout);
|
---|
135 | if(do_flush) { fflush(stdout); }
|
---|
136 | }
|
---|
137 | }
|
---|
138 | }
|
---|
139 | }
|
---|
140 |
|
---|
141 | static void log_list_flush(void)
|
---|
142 | {
|
---|
143 | pthread_cond_signal(&log_thread_sleep_cond);
|
---|
144 | int32_t i = 0;
|
---|
145 | while(ll_count(log_list) > 0 && i < 200)
|
---|
146 | {
|
---|
147 | cs_sleepms(5);
|
---|
148 | ++i;
|
---|
149 | }
|
---|
150 | }
|
---|
151 |
|
---|
152 | static void log_list_add(struct s_log *log)
|
---|
153 | {
|
---|
154 | int32_t count = ll_count(log_list);
|
---|
155 | log_list_queued++;
|
---|
156 | if(count < MAX_LOG_LIST_BACKLOG)
|
---|
157 | {
|
---|
158 | ll_append(log_list, log);
|
---|
159 | }
|
---|
160 | else // We have too much backlog
|
---|
161 | {
|
---|
162 | NULLFREE(log->txt);
|
---|
163 | NULLFREE(log);
|
---|
164 | cs_write_log("-------------> Too much data in log_list, dropping log message.\n", 1);
|
---|
165 | }
|
---|
166 | pthread_cond_signal(&log_thread_sleep_cond);
|
---|
167 | }
|
---|
168 |
|
---|
169 | static void cs_write_log_int(char *txt)
|
---|
170 | {
|
---|
171 | if(exit_oscam == 1)
|
---|
172 | {
|
---|
173 | cs_write_log(txt, 1);
|
---|
174 | }
|
---|
175 | else
|
---|
176 | {
|
---|
177 | char *newtxt = cs_strdup(txt);
|
---|
178 | if(!newtxt)
|
---|
179 | { return; }
|
---|
180 | struct s_log *log;
|
---|
181 | if(!cs_malloc(&log, sizeof(struct s_log)))
|
---|
182 | {
|
---|
183 | NULLFREE(newtxt);
|
---|
184 | return;
|
---|
185 | }
|
---|
186 | log->txt = newtxt;
|
---|
187 | log->header_len = 0;
|
---|
188 | log->direct_log = 1;
|
---|
189 | log_list_add(log);
|
---|
190 | }
|
---|
191 | }
|
---|
192 |
|
---|
193 | int32_t cs_open_logfiles(void)
|
---|
194 | {
|
---|
195 | char *starttext;
|
---|
196 | if(logStarted) { starttext = "log switched"; }
|
---|
197 | else { starttext = "started"; }
|
---|
198 | if(!fp && cfg.logfile) //log to file
|
---|
199 | {
|
---|
200 | if((fp = fopen(cfg.logfile, "a+")) <= (FILE *)0)
|
---|
201 | {
|
---|
202 | fp = (FILE *)0;
|
---|
203 | fprintf(stderr, "couldn't open logfile: %s (errno %d %s)\n", cfg.logfile, errno, strerror(errno));
|
---|
204 | }
|
---|
205 | else
|
---|
206 | {
|
---|
207 | char line[80];
|
---|
208 | memset(line, '-', sizeof(line));
|
---|
209 | line[(sizeof(line) / sizeof(char)) - 1] = '\0';
|
---|
210 | time_t walltime = cs_time();
|
---|
211 | if(!cfg.disablelog)
|
---|
212 | {
|
---|
213 | char buf[28];
|
---|
214 | cs_ctime_r(&walltime, buf);
|
---|
215 | fprintf(fp, "\n%s\n>> OSCam << cardserver %s at %s%s\n", line, starttext, buf, line);
|
---|
216 | }
|
---|
217 | }
|
---|
218 | }
|
---|
219 | // according to syslog docu: calling closelog is not necessary and calling openlog multiple times is safe
|
---|
220 | // We use openlog to set the default syslog settings so that it's possible to allow switching syslog on and off
|
---|
221 | openlog(syslog_ident, LOG_NDELAY | LOG_PID, LOG_DAEMON);
|
---|
222 | cs_log(">> OSCam << cardserver %s, version " CS_VERSION ", build r" CS_SVN_VERSION " (" CS_TARGET ")", starttext);
|
---|
223 |
|
---|
224 | return (fp <= (FILE *)0);
|
---|
225 | }
|
---|
226 |
|
---|
227 | #if defined(WEBIF) || defined(MODULE_MONITOR)
|
---|
228 | static uint64_t counter = 0;
|
---|
229 | static CS_MUTEX_LOCK loghistory_lock;
|
---|
230 | // These are accessed in module-monitor and module-webif
|
---|
231 | char *loghist = NULL; // ptr of log-history
|
---|
232 | char *loghistid = NULL;
|
---|
233 | char *loghistptr = NULL;
|
---|
234 |
|
---|
235 | /*
|
---|
236 | This function allows to reinit the in-memory loghistory with a new size.
|
---|
237 | */
|
---|
238 | void cs_reinit_loghist(uint32_t size)
|
---|
239 | {
|
---|
240 | char *tmp = NULL, *tmp2, *tmp3 = NULL, *tmp4;
|
---|
241 | if(size != cfg.loghistorysize)
|
---|
242 | {
|
---|
243 | if(cs_malloc(&tmp, size) && cs_malloc(&tmp3, size/3+8))
|
---|
244 | {
|
---|
245 | cs_writelock(&loghistory_lock);
|
---|
246 | tmp2 = loghist;
|
---|
247 | tmp4 = loghistid;
|
---|
248 | // On shrinking, the log is not copied and the order is reversed
|
---|
249 | if(size < cfg.loghistorysize)
|
---|
250 | {
|
---|
251 | cfg.loghistorysize = size;
|
---|
252 | cs_sleepms(20); // Monitor or webif may be currently outputting the loghistory but don't use locking so we sleep a bit...
|
---|
253 | loghistptr = tmp;
|
---|
254 | loghist = tmp;
|
---|
255 | loghistid = tmp3;
|
---|
256 | }
|
---|
257 | else
|
---|
258 | {
|
---|
259 | if(loghist)
|
---|
260 | {
|
---|
261 | memcpy(tmp, loghist, cfg.loghistorysize);
|
---|
262 | loghistptr = tmp + (loghistptr - loghist);
|
---|
263 | memcpy(tmp3, loghistid, cfg.loghistorysize/3);
|
---|
264 | } else {
|
---|
265 | loghistptr = tmp;
|
---|
266 | }
|
---|
267 | loghist = tmp;
|
---|
268 | loghistid = tmp3;
|
---|
269 | cs_sleepms(20); // Monitor or webif may be currently outputting the loghistory but don't use locking so we sleep a bit...
|
---|
270 | cfg.loghistorysize = size;
|
---|
271 | }
|
---|
272 | cs_writeunlock(&loghistory_lock);
|
---|
273 | if(tmp2 != NULL) { add_garbage(tmp2); }
|
---|
274 | if(tmp4 != NULL) { add_garbage(tmp4); }
|
---|
275 | }
|
---|
276 | }
|
---|
277 | }
|
---|
278 | #endif
|
---|
279 |
|
---|
280 | static struct timeb log_ts;
|
---|
281 |
|
---|
282 | static int32_t get_log_header(char *txt, int32_t txt_size)
|
---|
283 | {
|
---|
284 | struct s_client *cl = cur_client();
|
---|
285 | struct tm lt;
|
---|
286 |
|
---|
287 | cs_ftime(&log_ts);
|
---|
288 | time_t walltime = cs_walltime(&log_ts);
|
---|
289 | localtime_r(&walltime, <);
|
---|
290 |
|
---|
291 | return snprintf(txt, txt_size, "[LOG000]%4d/%02d/%02d %02d:%02d:%02d %8X %c ",
|
---|
292 | lt.tm_year + 1900,
|
---|
293 | lt.tm_mon + 1,
|
---|
294 | lt.tm_mday,
|
---|
295 | lt.tm_hour,
|
---|
296 | lt.tm_min,
|
---|
297 | lt.tm_sec,
|
---|
298 | cl ? cl->tid : 0,
|
---|
299 | cl ? cl->typ : ' '
|
---|
300 | );
|
---|
301 | }
|
---|
302 |
|
---|
303 | static void write_to_log(char *txt, struct s_log *log, int8_t do_flush)
|
---|
304 | {
|
---|
305 | (void)log; // Prevent warning when WEBIF, MODULE_MONITOR and CS_ANTICASC are disabled
|
---|
306 |
|
---|
307 | // anticascading messages go to their own log
|
---|
308 | if (!anticasc_logging(txt + 8))
|
---|
309 | {
|
---|
310 | if(cfg.logtosyslog)
|
---|
311 | { syslog(LOG_INFO, "%s", txt + 29); }
|
---|
312 | }
|
---|
313 | strcat(txt, "\n");
|
---|
314 | cs_write_log(txt + 8, do_flush);
|
---|
315 |
|
---|
316 | #if defined(WEBIF) || defined(MODULE_MONITOR)
|
---|
317 | if(loghist && !exit_oscam && cfg.loghistorysize)
|
---|
318 | {
|
---|
319 | char *usrtxt = log->cl_text;
|
---|
320 | char *target_ptr = NULL;
|
---|
321 | int32_t target_len = strlen(usrtxt) + (strlen(txt) - 8) + 1;
|
---|
322 |
|
---|
323 | cs_writelock(&loghistory_lock);
|
---|
324 | char *lastpos = loghist + (cfg.loghistorysize) - 1;
|
---|
325 | if(loghist + target_len + 1 >= lastpos)
|
---|
326 | {
|
---|
327 | 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!
|
---|
328 | target_len = strlen(usrtxt) + (strlen(txt) - 8) + 1;
|
---|
329 | }
|
---|
330 | if(!loghistptr)
|
---|
331 | { loghistptr = loghist; }
|
---|
332 |
|
---|
333 | if(loghistptr + target_len + 1 > lastpos)
|
---|
334 | {
|
---|
335 | *loghistptr = '\0';
|
---|
336 | loghistptr = loghist + target_len + 1;
|
---|
337 | *loghistptr = '\0';
|
---|
338 | target_ptr = loghist;
|
---|
339 | }
|
---|
340 | else
|
---|
341 | {
|
---|
342 | target_ptr = loghistptr;
|
---|
343 | loghistptr = loghistptr + target_len + 1;
|
---|
344 | *loghistptr = '\0';
|
---|
345 | }
|
---|
346 | ++counter;
|
---|
347 | cs_writeunlock(&loghistory_lock);
|
---|
348 | snprintf(target_ptr, target_len + 1, "%s\t%s", usrtxt, txt + 8);
|
---|
349 | ull2b_buf(counter, (uchar *)(loghistid + ((target_ptr-loghist)/3)));
|
---|
350 | }
|
---|
351 | #endif
|
---|
352 |
|
---|
353 | #if defined(MODULE_MONITOR)
|
---|
354 | char sbuf[16];
|
---|
355 | struct s_client *cl;
|
---|
356 | for(cl = first_client; cl ; cl = cl->next)
|
---|
357 | {
|
---|
358 | if((cl->typ == 'm') && (cl->monlvl > 0) && cl->log) //this variable is only initialized for cl->typ = 'm'
|
---|
359 | {
|
---|
360 | if(cl->monlvl < 2)
|
---|
361 | {
|
---|
362 | if(log->cl_typ != 'c' && log->cl_typ != 'm')
|
---|
363 | { continue; }
|
---|
364 | if(log->cl_usr && cl->account && strcmp(log->cl_usr, cl->account->usr))
|
---|
365 | { continue; }
|
---|
366 | }
|
---|
367 | snprintf(sbuf, sizeof(sbuf), "%03d", cl->logcounter);
|
---|
368 | cl->logcounter = (cl->logcounter + 1) % 1000;
|
---|
369 | memcpy(txt + 4, sbuf, 3);
|
---|
370 | monitor_send_idx(cl, txt);
|
---|
371 | }
|
---|
372 | }
|
---|
373 | #endif
|
---|
374 | }
|
---|
375 |
|
---|
376 | static void write_to_log_int(char *txt, int8_t header_len)
|
---|
377 | {
|
---|
378 | #if !defined(WEBIF) && !defined(MODULE_MONITOR)
|
---|
379 | if(cfg.disablelog) { return; }
|
---|
380 | #endif
|
---|
381 | char *newtxt = cs_strdup(txt);
|
---|
382 | if(!newtxt)
|
---|
383 | { return; }
|
---|
384 | struct s_log *log;
|
---|
385 | if(!cs_malloc(&log, sizeof(struct s_log)))
|
---|
386 | {
|
---|
387 | NULLFREE(newtxt);
|
---|
388 | return;
|
---|
389 | }
|
---|
390 | log->txt = newtxt;
|
---|
391 | log->header_len = header_len;
|
---|
392 | log->direct_log = 0;
|
---|
393 | struct s_client *cl = cur_client();
|
---|
394 | log->cl_usr = "";
|
---|
395 | if(!cl)
|
---|
396 | {
|
---|
397 | log->cl_text = "undef";
|
---|
398 | log->cl_typ = ' ';
|
---|
399 | }
|
---|
400 | else
|
---|
401 | {
|
---|
402 | switch(cl->typ)
|
---|
403 | {
|
---|
404 | case 'c':
|
---|
405 | case 'm':
|
---|
406 | if(cl->account)
|
---|
407 | {
|
---|
408 | log->cl_text = cl->account->usr;
|
---|
409 | log->cl_usr = cl->account->usr;
|
---|
410 | }
|
---|
411 | else { log->cl_text = ""; }
|
---|
412 | break;
|
---|
413 | case 'p':
|
---|
414 | case 'r':
|
---|
415 | log->cl_text = cl->reader ? cl->reader->label : "";
|
---|
416 | break;
|
---|
417 | default:
|
---|
418 | log->cl_text = "server";
|
---|
419 | break;
|
---|
420 | }
|
---|
421 | log->cl_typ = cl->typ;
|
---|
422 | }
|
---|
423 |
|
---|
424 | if(exit_oscam == 1 || cfg.disablelog) //Exit or log disabled. if disabled, just display on webif/monitor
|
---|
425 | {
|
---|
426 | char buf[LOG_BUF_SIZE];
|
---|
427 | cs_strncpy(buf, log->txt, LOG_BUF_SIZE);
|
---|
428 | write_to_log(buf, log, 1);
|
---|
429 | NULLFREE(log->txt);
|
---|
430 | NULLFREE(log);
|
---|
431 | }
|
---|
432 | else
|
---|
433 | { log_list_add(log); }
|
---|
434 | }
|
---|
435 |
|
---|
436 | static pthread_mutex_t log_mutex;
|
---|
437 | static char log_txt[LOG_BUF_SIZE];
|
---|
438 | static char dupl[LOG_BUF_SIZE / 4];
|
---|
439 | static char last_log_txt[LOG_BUF_SIZE];
|
---|
440 | static struct timeb last_log_ts;
|
---|
441 | static unsigned int last_log_duplicates;
|
---|
442 |
|
---|
443 | static void __cs_log_check_duplicates(int32_t hdr_len)
|
---|
444 | {
|
---|
445 | bool repeated_line = strcmp(last_log_txt, log_txt + hdr_len) == 0;
|
---|
446 | if (last_log_duplicates > 0)
|
---|
447 | {
|
---|
448 | if (!cs_valid_time(&last_log_ts)) // Must be initialized once
|
---|
449 | last_log_ts = log_ts;
|
---|
450 | // Report duplicated lines when the new log line is different
|
---|
451 | // than the old or 60 seconds have passed.
|
---|
452 | int64_t gone = comp_timeb(&log_ts, &last_log_ts);
|
---|
453 | if (!repeated_line || gone >= 60*1000)
|
---|
454 | {
|
---|
455 | int32_t dupl_header_len = get_log_header(dupl, sizeof(dupl));
|
---|
456 | snprintf(dupl + dupl_header_len - 1, sizeof(dupl) - dupl_header_len, " (-) -- Skipped %u duplicated log lines --", last_log_duplicates);
|
---|
457 | write_to_log_int(dupl, 0);
|
---|
458 | last_log_duplicates = 0;
|
---|
459 | last_log_ts = log_ts;
|
---|
460 | }
|
---|
461 | }
|
---|
462 | if (!repeated_line)
|
---|
463 | {
|
---|
464 | memcpy(last_log_txt, log_txt + hdr_len, LOG_BUF_SIZE - hdr_len);
|
---|
465 | write_to_log_int(log_txt, hdr_len);
|
---|
466 | } else {
|
---|
467 | last_log_duplicates++;
|
---|
468 | }
|
---|
469 | }
|
---|
470 |
|
---|
471 | #define __init_log_prefix(fmt) \
|
---|
472 | int32_t hdr_len = get_log_header(log_txt, sizeof(log_txt)); \
|
---|
473 | int32_t log_prefix_len = 0; \
|
---|
474 | do { \
|
---|
475 | if (log_prefix) { \
|
---|
476 | char _lp[16]; \
|
---|
477 | snprintf(_lp, sizeof(_lp), "(%s)", log_prefix); \
|
---|
478 | log_prefix_len = snprintf(log_txt + hdr_len, sizeof(log_txt) - hdr_len, fmt, _lp); \
|
---|
479 | } \
|
---|
480 | } while(0)
|
---|
481 |
|
---|
482 | #define __do_log() \
|
---|
483 | do { \
|
---|
484 | va_list params; \
|
---|
485 | va_start(params, fmt); \
|
---|
486 | __init_log_prefix("%10s "); \
|
---|
487 | vsnprintf(log_txt + hdr_len + log_prefix_len, sizeof(log_txt) - (hdr_len + log_prefix_len), fmt, params); \
|
---|
488 | va_end(params); \
|
---|
489 | if (cfg.logduplicatelines) \
|
---|
490 | { \
|
---|
491 | memcpy(last_log_txt, log_txt + hdr_len, LOG_BUF_SIZE - hdr_len); \
|
---|
492 | write_to_log_int(log_txt, hdr_len); \
|
---|
493 | } else { \
|
---|
494 | __cs_log_check_duplicates(hdr_len); \
|
---|
495 | } \
|
---|
496 | } while(0)
|
---|
497 |
|
---|
498 | void cs_log_txt(const char *log_prefix, const char *fmt, ...)
|
---|
499 | {
|
---|
500 | pthread_mutex_lock(&log_mutex);
|
---|
501 | __do_log();
|
---|
502 | pthread_mutex_unlock(&log_mutex);
|
---|
503 | }
|
---|
504 |
|
---|
505 | void cs_log_hex(const char *log_prefix, const uint8_t *buf, int32_t n, const char *fmt, ...)
|
---|
506 | {
|
---|
507 | pthread_mutex_lock(&log_mutex);
|
---|
508 | __do_log();
|
---|
509 | if(buf)
|
---|
510 | {
|
---|
511 | int32_t i;
|
---|
512 | __init_log_prefix("%10s ");
|
---|
513 | for(i = 0; i < n; i += 16)
|
---|
514 | {
|
---|
515 | 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));
|
---|
516 | write_to_log_int(log_txt, hdr_len);
|
---|
517 | }
|
---|
518 | }
|
---|
519 | pthread_mutex_unlock(&log_mutex);
|
---|
520 | }
|
---|
521 |
|
---|
522 | static void cs_close_log(void)
|
---|
523 | {
|
---|
524 | log_list_flush();
|
---|
525 | if(fp)
|
---|
526 | {
|
---|
527 | fclose(fp);
|
---|
528 | fp = (FILE *)0;
|
---|
529 | }
|
---|
530 | }
|
---|
531 |
|
---|
532 | int32_t cs_init_statistics(void)
|
---|
533 | {
|
---|
534 | if((!fps) && (cfg.usrfile != NULL))
|
---|
535 | {
|
---|
536 | if((fps = fopen(cfg.usrfile, "a+")) <= (FILE *)0)
|
---|
537 | {
|
---|
538 | fps = (FILE *)0;
|
---|
539 | cs_log("couldn't open statistics file: %s", cfg.usrfile);
|
---|
540 | }
|
---|
541 | }
|
---|
542 | return (fps <= (FILE *)0);
|
---|
543 | }
|
---|
544 |
|
---|
545 | void cs_statistics(struct s_client *client)
|
---|
546 | {
|
---|
547 | if(!cfg.disableuserfile)
|
---|
548 | {
|
---|
549 | struct tm lt;
|
---|
550 | char buf[LOG_BUF_SIZE];
|
---|
551 |
|
---|
552 | float cwps;
|
---|
553 |
|
---|
554 | time_t walltime = cs_time();
|
---|
555 | localtime_r(&walltime, <);
|
---|
556 | if(client->cwfound + client->cwnot > 0)
|
---|
557 | {
|
---|
558 | cwps = client->last - client->login;
|
---|
559 | cwps /= client->cwfound + client->cwnot;
|
---|
560 | }
|
---|
561 | else
|
---|
562 | { cwps = 0; }
|
---|
563 |
|
---|
564 | char channame[32];
|
---|
565 | get_servicename(client, client->last_srvid, client->last_caid, channame);
|
---|
566 |
|
---|
567 | int32_t lsec;
|
---|
568 | if((client->last_caid == NO_CAID_VALUE) && (client->last_srvid == NO_SRVID_VALUE))
|
---|
569 | { lsec = client->last - client->login; } //client leave calc total duration
|
---|
570 | else
|
---|
571 | { lsec = client->last - client->lastswitch; }
|
---|
572 |
|
---|
573 | int32_t secs = 0, fullmins = 0, mins = 0, fullhours = 0;
|
---|
574 |
|
---|
575 | if((lsec > 0) && (lsec < 1000000))
|
---|
576 | {
|
---|
577 | secs = lsec % 60;
|
---|
578 | if(lsec > 60)
|
---|
579 | {
|
---|
580 | fullmins = lsec / 60;
|
---|
581 | mins = fullmins % 60;
|
---|
582 | if(fullmins > 60)
|
---|
583 | {
|
---|
584 | fullhours = fullmins / 60;
|
---|
585 | }
|
---|
586 | }
|
---|
587 | }
|
---|
588 |
|
---|
589 | /* statistics entry start with 's' to filter it out on other end of pipe
|
---|
590 | * so we can use the same Pipe as Log
|
---|
591 | */
|
---|
592 | 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",
|
---|
593 | lt.tm_mday, lt.tm_mon + 1, lt.tm_year % 100,
|
---|
594 | lt.tm_hour, lt.tm_min, lt.tm_sec, cwps,
|
---|
595 | client->account->usr,
|
---|
596 | cs_inet_ntoa(client->ip),
|
---|
597 | client->port,
|
---|
598 | client->cwfound,
|
---|
599 | client->cwcache,
|
---|
600 | client->cwnot,
|
---|
601 | client->cwignored,
|
---|
602 | client->cwtout,
|
---|
603 | client->cwtun,
|
---|
604 | client->login,
|
---|
605 | client->last,
|
---|
606 | fullhours, mins, secs,
|
---|
607 | get_module(client)->desc,
|
---|
608 | client->last_caid,
|
---|
609 | client->last_srvid,
|
---|
610 | channame);
|
---|
611 |
|
---|
612 | cs_write_log_int(buf);
|
---|
613 | }
|
---|
614 | }
|
---|
615 |
|
---|
616 | void log_list_thread(void)
|
---|
617 | {
|
---|
618 | char buf[LOG_BUF_SIZE];
|
---|
619 | log_running = 1;
|
---|
620 | set_thread_name(__func__);
|
---|
621 | do
|
---|
622 | {
|
---|
623 | log_list_queued = 0;
|
---|
624 | LL_ITER it = ll_iter_create(log_list);
|
---|
625 | struct s_log *log;
|
---|
626 | while((log = ll_iter_next_remove(&it)))
|
---|
627 | {
|
---|
628 | int8_t do_flush = ll_count(log_list) == 0; //flush on writing last element
|
---|
629 |
|
---|
630 | cs_strncpy(buf, log->txt, LOG_BUF_SIZE);
|
---|
631 | if(log->direct_log)
|
---|
632 | { cs_write_log(buf, do_flush); }
|
---|
633 | else
|
---|
634 | { write_to_log(buf, log, do_flush); }
|
---|
635 | NULLFREE(log->txt);
|
---|
636 | NULLFREE(log);
|
---|
637 | }
|
---|
638 | if(!log_list_queued) // The list is empty, sleep until new data comes in and we are woken up
|
---|
639 | sleepms_on_cond(&log_thread_sleep_cond_mutex, &log_thread_sleep_cond, 60 * 1000);
|
---|
640 | }
|
---|
641 | while(log_running);
|
---|
642 | ll_destroy(&log_list);
|
---|
643 | }
|
---|
644 |
|
---|
645 | int32_t cs_init_log(void)
|
---|
646 | {
|
---|
647 | if(logStarted == 0)
|
---|
648 | {
|
---|
649 | pthread_mutex_init(&log_mutex, NULL);
|
---|
650 |
|
---|
651 | cs_pthread_cond_init(&log_thread_sleep_cond_mutex, &log_thread_sleep_cond);
|
---|
652 |
|
---|
653 | #if defined(WEBIF) || defined(MODULE_MONITOR)
|
---|
654 | cs_lock_create(&loghistory_lock, "loghistory_lock", 5000);
|
---|
655 | #endif
|
---|
656 |
|
---|
657 | log_list = ll_create(LOG_LIST);
|
---|
658 | pthread_attr_t attr;
|
---|
659 | pthread_attr_init(&attr);
|
---|
660 | pthread_attr_setstacksize(&attr, PTHREAD_STACK_SIZE);
|
---|
661 | int32_t ret = pthread_create(&log_thread, &attr, (void *)&log_list_thread, NULL);
|
---|
662 | if(ret)
|
---|
663 | {
|
---|
664 | fprintf(stderr, "ERROR: Can't create logging thread (errno=%d %s)", ret, strerror(ret));
|
---|
665 | pthread_attr_destroy(&attr);
|
---|
666 | cs_exit(1);
|
---|
667 | }
|
---|
668 | pthread_attr_destroy(&attr);
|
---|
669 | }
|
---|
670 | int32_t rc = 0;
|
---|
671 | if(!cfg.disablelog) { rc = cs_open_logfiles(); }
|
---|
672 | logStarted = 1;
|
---|
673 | return rc;
|
---|
674 | }
|
---|
675 |
|
---|
676 | void cs_disable_log(int8_t disabled)
|
---|
677 | {
|
---|
678 | if(cfg.disablelog != disabled)
|
---|
679 | {
|
---|
680 | if(disabled && logStarted)
|
---|
681 | {
|
---|
682 | cs_log("Stopping log...");
|
---|
683 | log_list_flush();
|
---|
684 | }
|
---|
685 | cfg.disablelog = disabled;
|
---|
686 | if(disabled)
|
---|
687 | {
|
---|
688 | if(logStarted)
|
---|
689 | {
|
---|
690 | cs_sleepms(20);
|
---|
691 | cs_close_log();
|
---|
692 | }
|
---|
693 | }
|
---|
694 | else
|
---|
695 | {
|
---|
696 | cs_open_logfiles();
|
---|
697 | }
|
---|
698 | }
|
---|
699 | }
|
---|
700 |
|
---|
701 | void log_free(void)
|
---|
702 | {
|
---|
703 | cs_close_log();
|
---|
704 | log_running = 0;
|
---|
705 | pthread_cond_signal(&log_thread_sleep_cond);
|
---|
706 | pthread_join(log_thread, NULL);
|
---|
707 | #if defined(WEBIF) || defined(MODULE_MONITOR)
|
---|
708 | NULLFREE(loghist);
|
---|
709 | NULLFREE(loghistid);
|
---|
710 | loghist = loghistptr = loghistid = NULL;
|
---|
711 | #endif
|
---|
712 | }
|
---|