1 /*
2 bctoolbox
3 Copyright (C) 2016  Belledonne Communications SARL
4 
5 
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10 
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 Lesser General Public License for more details.
15 
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "bctoolbox/logging.h"
26 #include <time.h>
27 #include <stdio.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 
31 #ifdef _MSC_VER
32 #ifndef access
33 #define access _access
34 #endif
35 #ifndef fileno
36 #define fileno _fileno
37 #endif
38 #endif
39 
40 
41 typedef struct{
42 	char *domain;
43 	unsigned int logmask;
44 }BctoolboxLogDomain;
45 
bctbx_log_domain_destroy(BctoolboxLogDomain * obj)46 static void bctbx_log_domain_destroy(BctoolboxLogDomain *obj){
47 	if (obj->domain) bctbx_free(obj->domain);
48 	bctbx_free(obj);
49 }
50 
51 typedef struct _bctbx_logger_t {
52 	bctbx_list_t *logv_outs;
53 	unsigned int log_mask; /*the default log mask, if no per-domain settings are found*/
54 	unsigned long log_thread_id;
55 	bctbx_list_t *log_stored_messages_list;
56 	bctbx_list_t *log_domains;
57 	bctbx_mutex_t log_stored_messages_mutex;
58 	bctbx_mutex_t domains_mutex;
59 	bctbx_mutex_t log_mutex;
60 } bctbx_logger_t;
61 
62 struct _bctbx_log_handler_t {
63 	BctbxLogHandlerFunc func;
64 	BctbxLogHandlerDestroyFunc destroy;
65 	void* user_info;
66 };
67 
68 typedef struct _bctbx_file_log_handler_t {
69 	char* path;
70 	char* name;
71 	uint64_t max_size;
72 	uint64_t size;
73 	FILE* file;
74 } bctbx_file_log_handler_t;
75 
76 static bctbx_logger_t __bctbx_logger = { NULL, BCTBX_LOG_WARNING|BCTBX_LOG_ERROR|BCTBX_LOG_FATAL, 0};
77 
78 static unsigned int bctbx_init_logger_refcount = 0;
79 
bctbx_init_logger(bool_t create)80 void bctbx_init_logger(bool_t create){
81 	if (bctbx_init_logger_refcount++ > 0) return; /*already initialized*/
82 
83 	bctbx_mutex_init(&__bctbx_logger.domains_mutex, NULL);
84 	bctbx_mutex_init(&__bctbx_logger.log_mutex, NULL);
85 	if(create) {
86 		bctbx_log_handler_t* handler = bctbx_create_log_handler(bctbx_logv_out, bctbx_logv_out_destroy, NULL);
87 		bctbx_add_log_handler(handler);
88 	}
89 }
90 
bctbx_log_handlers_free(void)91 void bctbx_log_handlers_free(void) {
92 	bctbx_list_t *loggers = bctbx_list_first_elem(__bctbx_logger.logv_outs);
93 	while (loggers) {
94 		bctbx_log_handler_t* handler = (bctbx_log_handler_t*)loggers->data;
95 		handler->destroy(handler);
96 		loggers = loggers->next;
97 	}
98 }
99 
bctbx_uninit_logger(void)100 void bctbx_uninit_logger(void){
101 	if (--bctbx_init_logger_refcount <= 0) {
102 		bctbx_logv_flush();
103 		bctbx_mutex_destroy(&__bctbx_logger.domains_mutex);
104 		bctbx_mutex_destroy(&__bctbx_logger.log_mutex);
105 		bctbx_log_handlers_free();
106 		__bctbx_logger.logv_outs = bctbx_list_free(__bctbx_logger.logv_outs);
107 		__bctbx_logger.log_domains = bctbx_list_free_with_data(__bctbx_logger.log_domains, (void (*)(void*))bctbx_log_domain_destroy);
108 	}
109 }
110 
bctbx_create_log_handler(BctbxLogHandlerFunc func,BctbxLogHandlerDestroyFunc destroy,void * user_info)111 bctbx_log_handler_t* bctbx_create_log_handler(BctbxLogHandlerFunc func, BctbxLogHandlerDestroyFunc destroy, void* user_info) {
112 	bctbx_log_handler_t* handler = (bctbx_log_handler_t*)bctbx_malloc(sizeof(bctbx_log_handler_t));
113 	handler->func = func;
114 	handler->destroy = destroy;
115 	handler->user_info = user_info;
116 	return handler;
117 }
118 
bctbx_create_file_log_handler(uint64_t max_size,const char * path,const char * name,FILE * f)119 bctbx_log_handler_t* bctbx_create_file_log_handler(uint64_t max_size, const char* path, const char* name, FILE* f) {
120 	bctbx_log_handler_t* handler = (bctbx_log_handler_t*)bctbx_malloc0(sizeof(bctbx_log_handler_t));
121 	bctbx_file_log_handler_t* filehandler = (bctbx_file_log_handler_t*)bctbx_malloc(sizeof(bctbx_file_log_handler_t));
122 	char *full_name = bctbx_strdup_printf("%s/%s",
123 									path,
124 									name);
125 	struct stat buf;
126 	memset(&buf, 0, sizeof(buf));
127 	handler->func=bctbx_logv_file;
128 	handler->destroy=bctbx_logv_file_destroy;
129 	filehandler->max_size = max_size;
130 	// init with actual file size
131 	if(stat(full_name, &buf) != 0) {
132 		fprintf(stderr,"Error while creating file log handler. \n");
133 		return NULL;
134 	}
135 	bctbx_free(full_name);
136 	filehandler->size = buf.st_size;
137 	filehandler->path = bctbx_strdup(path);
138 	filehandler->name = bctbx_strdup(name);
139 	filehandler->file = f;
140 	handler->user_info=(void*) filehandler;
141 	return handler;
142 }
143 
144 /**
145 *@param func: your logging function, compatible with the BctoolboxLogFunc prototype.
146 *
147 **/
bctbx_add_log_handler(bctbx_log_handler_t * handler)148 void bctbx_add_log_handler(bctbx_log_handler_t* handler){
149 	if (handler && !bctbx_list_find(__bctbx_logger.logv_outs, handler))
150 		__bctbx_logger.logv_outs = bctbx_list_append(__bctbx_logger.logv_outs, (void*)handler);
151 	/*else, already in*/
152 }
153 
wrapper(void * info,const char * domain,BctbxLogLevel lev,const char * fmt,va_list args)154 static void wrapper(void* info,const char *domain, BctbxLogLevel lev, const char *fmt, va_list args) {
155 	BctbxLogFunc func = (BctbxLogFunc)info;
156 	func(domain, lev, fmt,  args);
157 }
158 
bctbx_set_log_handler(BctbxLogFunc func)159 void bctbx_set_log_handler(BctbxLogFunc func){
160 	static bctbx_log_handler_t handler;
161 	handler.func=wrapper;
162 	handler.destroy=(BctbxLogHandlerDestroyFunc)bctbx_logv_out_destroy;
163 	handler.user_info=(void*)func;
164 	bctbx_add_log_handler(&handler);
165 }
166 
bctbx_set_log_file(FILE * f)167 void bctbx_set_log_file(FILE* f){
168 	static bctbx_file_log_handler_t filehandler;
169 	static bctbx_log_handler_t handler;
170 	handler.func=bctbx_logv_file;
171 	handler.destroy=(BctbxLogHandlerDestroyFunc)bctbx_logv_file_destroy;
172 	filehandler.max_size = -1;
173 	filehandler.file = f;
174 	handler.user_info=(void*) &filehandler;
175 	bctbx_add_log_handler(&handler);
176 }
177 
bctbx_get_log_handlers(void)178 bctbx_list_t* bctbx_get_log_handlers(void){
179 	return __bctbx_logger.logv_outs;
180 }
181 
get_log_domain(const char * domain)182 static BctoolboxLogDomain * get_log_domain(const char *domain){
183 	bctbx_list_t *it;
184 
185 	if (domain == NULL) return NULL;
186 	for (it = __bctbx_logger.log_domains; it != NULL; it = bctbx_list_next(it)) {
187 		BctoolboxLogDomain *ld = (BctoolboxLogDomain*)bctbx_list_get_data(it);
188 		if (ld->domain && strcmp(ld->domain, domain) == 0 ){
189 			return ld;
190 		}
191 	}
192 	return NULL;
193 }
194 
get_log_domain_rw(const char * domain)195 static BctoolboxLogDomain *get_log_domain_rw(const char *domain){
196 	BctoolboxLogDomain *ret;
197 
198 	if (domain == NULL) return NULL;
199 	ret = get_log_domain(domain);
200 	if (ret) return ret;
201 	/*it does not exist, hence create it by taking the mutex*/
202 	bctbx_mutex_lock(&__bctbx_logger.domains_mutex);
203 	ret = get_log_domain(domain);
204 	if (!ret){
205 		ret = bctbx_new0(BctoolboxLogDomain,1);
206 		ret->domain = bctbx_strdup(domain);
207 		ret->logmask = __bctbx_logger.log_mask;
208 		__bctbx_logger.log_domains = bctbx_list_prepend(__bctbx_logger.log_domains, ret);
209 	}
210 	bctbx_mutex_unlock(&__bctbx_logger.domains_mutex);
211 	return ret;
212 }
213 
214 /**
215 * @ param levelmask a mask of BCTBX_DEBUG, BCTBX_MESSAGE, BCTBX_WARNING, BCTBX_ERROR
216 * BCTBX_FATAL .
217 **/
bctbx_set_log_level_mask(const char * domain,int levelmask)218 void bctbx_set_log_level_mask(const char *domain, int levelmask){
219 	if (domain == NULL) __bctbx_logger.log_mask=levelmask;
220 	else get_log_domain_rw(domain)->logmask = levelmask;
221 }
222 
223 
bctbx_set_log_level(const char * domain,BctbxLogLevel level)224 void bctbx_set_log_level(const char *domain, BctbxLogLevel level){
225 	int levelmask = BCTBX_LOG_FATAL;
226 	if (level<=BCTBX_LOG_ERROR){
227 		levelmask |= BCTBX_LOG_ERROR;
228 	}
229 	if (level<=BCTBX_LOG_WARNING){
230 		levelmask |= BCTBX_LOG_WARNING;
231 	}
232 	if (level<=BCTBX_LOG_MESSAGE){
233 		levelmask |= BCTBX_LOG_MESSAGE;
234 	}
235 	if (level<=BCTBX_LOG_TRACE)	{
236 		levelmask |= BCTBX_LOG_TRACE;
237 	}
238 	if (level<=BCTBX_LOG_DEBUG){
239 		levelmask |= BCTBX_LOG_DEBUG;
240 	}
241 	bctbx_set_log_level_mask(domain, levelmask);
242 }
243 
bctbx_get_log_level_mask(const char * domain)244 unsigned int bctbx_get_log_level_mask(const char *domain) {
245 	BctoolboxLogDomain *ld;
246 	if (domain == NULL || (ld = get_log_domain(domain)) == NULL) return __bctbx_logger.log_mask;
247 	else return ld->logmask;
248 }
249 
bctbx_set_log_thread_id(unsigned long thread_id)250 void bctbx_set_log_thread_id(unsigned long thread_id) {
251 	if (thread_id == 0) {
252 		bctbx_logv_flush();
253 		bctbx_mutex_destroy(&__bctbx_logger.log_stored_messages_mutex);
254 	} else {
255 		bctbx_mutex_init(&__bctbx_logger.log_stored_messages_mutex, NULL);
256 	}
257 	__bctbx_logger.log_thread_id = thread_id;
258 }
259 
bctbx_strdup_vprintf(const char * fmt,va_list ap)260 char * bctbx_strdup_vprintf(const char *fmt, va_list ap)
261 {
262 /* Guess we need no more than 100 bytes. */
263 	int n, size = 200;
264 	char *p,*np;
265 #ifndef _WIN32
266 	va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
267 #endif
268 	if ((p = (char *) bctbx_malloc (size)) == NULL)
269 		return NULL;
270 	while (1){
271 /* Try to print in the allocated space. */
272 #ifndef _WIN32
273 		va_copy(cap,ap);
274 		n = vsnprintf (p, size, fmt, cap);
275 		va_end(cap);
276 #else
277 		/*this works on 32 bits, luckily*/
278 		n = vsnprintf (p, size, fmt, ap);
279 #endif
280 		/* If that worked, return the string. */
281 		if (n > -1 && n < size)
282 			return p;
283 //printf("Reallocing space.\n");
284 		/* Else try again with more space. */
285 		if (n > -1)	/* glibc 2.1 */
286 			size = n + 1;	/* precisely what is needed */
287 		else		/* glibc 2.0 */
288 			size *= 2;	/* twice the old size */
289 		if ((np = (char *) bctbx_realloc (p, size)) == NULL)
290 		{
291 			free(p);
292 			return NULL;
293 		} else {
294 			p = np;
295 		}
296 	}
297 }
298 
bctbx_strdup_printf(const char * fmt,...)299 char *bctbx_strdup_printf(const char *fmt,...){
300 	char *ret;
301 	va_list args;
302 	va_start (args, fmt);
303 	ret=bctbx_strdup_vprintf(fmt, args);
304 	va_end (args);
305 	return ret;
306 }
307 
bctbx_strcat_vprintf(char * dst,const char * fmt,va_list ap)308 char * bctbx_strcat_vprintf(char* dst, const char *fmt, va_list ap){
309 	char *ret;
310 	size_t dstlen, retlen;
311 
312 	ret=bctbx_strdup_vprintf(fmt, ap);
313 	if (!dst) return ret;
314 
315 	dstlen = strlen(dst);
316 	retlen = strlen(ret);
317 
318 	if ((dst = bctbx_realloc(dst, dstlen+retlen+1)) != NULL){
319 		strncat(dst,ret,retlen);
320 		dst[dstlen+retlen] = '\0';
321 		bctbx_free(ret);
322 		return dst;
323 	} else {
324 		bctbx_free(ret);
325 		return NULL;
326 	}
327 }
328 
bctbx_strcat_printf(char * dst,const char * fmt,...)329 char *bctbx_strcat_printf(char* dst, const char *fmt,...){
330 	char *ret;
331 	va_list args;
332 	va_start (args, fmt);
333 	ret=bctbx_strcat_vprintf(dst, fmt, args);
334 	va_end (args);
335 	return ret;
336 }
337 
338 #if	defined(_WIN32) || defined(_WIN32_WCE)
339 #define ENDLINE "\r\n"
340 #else
341 #define ENDLINE "\n"
342 #endif
343 
344 typedef struct {
345 	int level;
346 	char *msg;
347 	char *domain;
348 } bctbx_stored_log_t;
349 
_bctbx_logv_flush(int dummy,...)350 void _bctbx_logv_flush(int dummy, ...) {
351 	bctbx_list_t *elem;
352 	bctbx_list_t *msglist;
353 	va_list empty_va_list;
354 	va_start(empty_va_list, dummy);
355 	bctbx_mutex_lock(&__bctbx_logger.log_stored_messages_mutex);
356 	msglist = __bctbx_logger.log_stored_messages_list;
357 	__bctbx_logger.log_stored_messages_list = NULL;
358 	bctbx_mutex_unlock(&__bctbx_logger.log_stored_messages_mutex);
359 	for (elem = msglist; elem != NULL; elem = bctbx_list_next(elem)) {
360 		bctbx_stored_log_t *l = (bctbx_stored_log_t *)bctbx_list_get_data(elem);
361 		bctbx_list_t *loggers = bctbx_list_first_elem(__bctbx_logger.logv_outs);
362 #ifdef _WIN32
363 		while (loggers) {
364 			bctbx_log_handler_t* handler = (bctbx_log_handler_t*)loggers->data;
365 			if(handler) {
366 				va_list cap;
367 				va_copy(cap, empty_va_list);
368 				handler->func(handler->user_info, l->domain, l->level, l->msg, cap);
369 				va_end(cap);
370 			}
371 			loggers = loggers->next;
372 		}
373 #else
374 
375 		while (loggers) {
376 			bctbx_log_handler_t* handler = (bctbx_log_handler_t*)loggers->data;
377 			if(handler) {
378 				va_list cap;
379 				va_copy(cap, empty_va_list);
380 				handler->func(handler->user_info, l->domain, l->level, l->msg, cap);
381 				va_end(cap);
382 			}
383 			loggers = loggers->next;
384 		}
385 
386 #endif
387 		if (l->domain) bctbx_free(l->domain);
388 		bctbx_free(l->msg);
389 		bctbx_free(l);
390 	}
391 	bctbx_list_free(msglist);
392 	va_end(empty_va_list);
393 }
394 
bctbx_logv_flush(void)395 void bctbx_logv_flush(void) {
396 	_bctbx_logv_flush(0);
397 }
398 
bctbx_logv(const char * domain,BctbxLogLevel level,const char * fmt,va_list args)399 void bctbx_logv(const char *domain, BctbxLogLevel level, const char *fmt, va_list args) {
400 	if ((__bctbx_logger.logv_outs != NULL) && bctbx_log_level_enabled(domain, level)) {
401 		if (__bctbx_logger.log_thread_id == 0) {
402 			bctbx_list_t *loggers = bctbx_list_first_elem(__bctbx_logger.logv_outs);
403 			while (loggers) {
404 				bctbx_log_handler_t* handler = (bctbx_log_handler_t*)loggers->data;
405 				if(handler) {
406 					va_list tmp;
407 					va_copy(tmp, args);
408 					handler->func(handler->user_info, domain, level, fmt, tmp);
409 					va_end(tmp);
410 				}
411 				loggers = loggers->next;
412 			}
413 		} else if (__bctbx_logger.log_thread_id == bctbx_thread_self()) {
414 			bctbx_list_t *loggers;
415 			bctbx_logv_flush();
416 			loggers = bctbx_list_first_elem(__bctbx_logger.logv_outs);
417 			while (loggers) {
418 				bctbx_log_handler_t* handler = (bctbx_log_handler_t*)loggers->data;
419 				if(handler) {
420 					va_list tmp;
421 					va_copy(tmp, args);
422 					handler->func(handler->user_info, domain, level, fmt, tmp);
423 					va_end(tmp);
424 				}
425 				loggers = loggers->next;
426 			}
427 		} else {
428 			bctbx_stored_log_t *l = bctbx_new(bctbx_stored_log_t, 1);
429 			l->domain = domain ? bctbx_strdup(domain) : NULL;
430 			l->level = level;
431 			l->msg = bctbx_strdup_vprintf(fmt, args);
432 			bctbx_mutex_lock(&__bctbx_logger.log_stored_messages_mutex);
433 			__bctbx_logger.log_stored_messages_list = bctbx_list_append(__bctbx_logger.log_stored_messages_list, l);
434 			bctbx_mutex_unlock(&__bctbx_logger.log_stored_messages_mutex);
435 		}
436 	}
437 #if !defined(_WIN32_WCE)
438 	if (level == BCTBX_LOG_FATAL) {
439 		bctbx_logv_flush();
440 		abort();
441 	}
442 #endif
443 }
444 
445 /*This function does the default formatting and output to file*/
bctbx_logv_out(void * user_info,const char * domain,BctbxLogLevel lev,const char * fmt,va_list args)446 void bctbx_logv_out(void* user_info, const char *domain, BctbxLogLevel lev, const char *fmt, va_list args){
447 	const char *lname="undef";
448 	char *msg;
449 	struct timeval tp;
450 	struct tm *lt;
451 #ifndef _WIN32
452 	struct tm tmbuf;
453 #endif
454 	time_t tt;
455 	FILE *std = stdout;
456 	bctbx_gettimeofday(&tp,NULL);
457 	tt = (time_t)tp.tv_sec;
458 
459 #ifdef _WIN32
460 	lt = localtime(&tt);
461 #else
462 	lt = localtime_r(&tt,&tmbuf);
463 #endif
464 
465 	switch(lev){
466 		case BCTBX_LOG_DEBUG:
467 			lname = "debug";
468 		break;
469 		case BCTBX_LOG_MESSAGE:
470 			lname = "message";
471 		break;
472 		case BCTBX_LOG_WARNING:
473 			lname = "warning";
474 		break;
475 		case BCTBX_LOG_ERROR:
476 			lname = "error";
477 			std = stderr;
478 		break;
479 		case BCTBX_LOG_FATAL:
480 			lname = "fatal";
481 			std = stderr;
482 		break;
483 		default:
484 			lname = "badlevel";
485 	}
486 
487 	msg=bctbx_strdup_vprintf(fmt,args);
488 #if defined(_MSC_VER) && !defined(_WIN32_WCE)
489 #ifndef _UNICODE
490 	OutputDebugStringA(msg);
491 	OutputDebugStringA("\r\n");
492 #else
493 	{
494 		size_t len=strlen(msg);
495 		wchar_t *tmp=(wchar_t*)bctbx_malloc0((len+1)*sizeof(wchar_t));
496 		mbstowcs(tmp,msg,len);
497 		OutputDebugStringW(tmp);
498 		OutputDebugStringW(L"\r\n");
499 		bctbx_free(tmp);
500 	}
501 #endif
502 #endif
503 	fprintf(std,"%i-%.2i-%.2i %.2i:%.2i:%.2i:%.3i %s-%s-%s" ENDLINE
504 			,1900+lt->tm_year,1+lt->tm_mon,lt->tm_mday,lt->tm_hour,lt->tm_min,lt->tm_sec
505 		,(int)(tp.tv_usec/1000), (domain?domain:"bctoolbox"), lname, msg);
506 	fflush(std);
507 	bctbx_free(msg);
508 }
509 
bctbx_logv_out_destroy(bctbx_log_handler_t * handler)510 void bctbx_logv_out_destroy(bctbx_log_handler_t* handler) {
511 	handler->user_info=NULL;
512 }
513 
_try_open_log_collection_file(bctbx_file_log_handler_t * filehandler)514 static int _try_open_log_collection_file(bctbx_file_log_handler_t *filehandler) {
515 	struct stat statbuf;
516 	char *log_filename;
517 
518 	log_filename = bctbx_strdup_printf("%s/%s",
519 		filehandler->path,
520 		filehandler->name);
521 	filehandler->file = fopen(log_filename, "a");
522 	bctbx_free(log_filename);
523 	if (filehandler->file == NULL) return -1;
524 
525 	fstat(fileno(filehandler->file), &statbuf);
526 	if ((uint64_t)statbuf.st_size > filehandler->max_size) {
527 		fclose(filehandler->file);
528 		return -1;
529 	}
530 
531 	filehandler->size = statbuf.st_size;
532 	return 0;
533 }
534 
_rotate_log_collection_files(bctbx_file_log_handler_t * filehandler)535 static void _rotate_log_collection_files(bctbx_file_log_handler_t *filehandler) {
536 	char *log_filename;
537 	char *log_filename2;
538 	char *file_no_extension = bctbx_strdup(filehandler->name);
539 	char *extension = strrchr(file_no_extension, '.');
540 	char *extension2 = bctbx_strdup(extension);
541 	int n = 1;
542 	file_no_extension[extension - file_no_extension] = '\0';
543 
544 	log_filename = bctbx_strdup_printf("%s/%s_1%s",
545 		filehandler->path,
546 		file_no_extension,
547 		extension2);
548 	while(access(log_filename, F_OK) != -1) {
549 		// file exists
550 		n++;
551 		log_filename = bctbx_strdup_printf("%s/%s_%d%s",
552 		filehandler->path,
553 		file_no_extension,
554 		n,
555 		extension2);
556 	}
557 
558 	while(n > 1) {
559 		log_filename = bctbx_strdup_printf("%s/%s_%d%s",
560 		filehandler->path,
561 		file_no_extension,
562 		n-1,
563 		extension2);
564 		log_filename2 = bctbx_strdup_printf("%s/%s_%d%s",
565 		filehandler->path,
566 		file_no_extension,
567 		n,
568 		extension2);
569 
570 		n--;
571 		rename(log_filename, log_filename2);
572 	}
573 
574 	log_filename = bctbx_strdup_printf("%s/%s",
575 	filehandler->path,
576 	filehandler->name);
577 	log_filename2 = bctbx_strdup_printf("%s/%s_1%s",
578 	filehandler->path,
579 	file_no_extension,
580 	extension2);
581 	rename(log_filename, log_filename2);
582 	bctbx_free(log_filename);
583 	bctbx_free(log_filename2);
584 	bctbx_free(extension2);
585 	bctbx_free(file_no_extension);
586 }
587 
_open_log_collection_file(bctbx_file_log_handler_t * filehandler)588 static void _open_log_collection_file(bctbx_file_log_handler_t *filehandler) {
589 	if (_try_open_log_collection_file(filehandler) < 0) {
590 		_rotate_log_collection_files(filehandler);
591 		_try_open_log_collection_file(filehandler);
592 	}
593 }
594 
_close_log_collection_file(bctbx_file_log_handler_t * filehandler)595 static void _close_log_collection_file(bctbx_file_log_handler_t *filehandler) {
596 	if (filehandler->file) {
597 		fclose(filehandler->file);
598 		filehandler->file = NULL;
599 		filehandler->size = 0;
600 	}
601 }
602 
bctbx_logv_file(void * user_info,const char * domain,BctbxLogLevel lev,const char * fmt,va_list args)603 void bctbx_logv_file(void* user_info, const char *domain, BctbxLogLevel lev, const char *fmt, va_list args){
604 	const char *lname="undef";
605 	char *msg;
606 	struct timeval tp;
607 	struct tm *lt;
608 #ifndef _WIN32
609 	struct tm tmbuf;
610 #endif
611 	time_t tt;
612 	int ret = -1;
613 	bctbx_file_log_handler_t *filehandler = (bctbx_file_log_handler_t *) user_info;
614 	FILE *f;
615 	bctbx_mutex_lock(&__bctbx_logger.log_mutex);
616 	f = filehandler->file;
617 	bctbx_gettimeofday(&tp,NULL);
618 	tt = (time_t)tp.tv_sec;
619 
620 #ifdef _WIN32
621 	lt = localtime(&tt);
622 #else
623 	lt = localtime_r(&tt,&tmbuf);
624 #endif
625 
626 	if(!f) {
627 		return;
628 	}
629 	switch(lev){
630 		case BCTBX_LOG_DEBUG:
631 			lname = "debug";
632 		break;
633 		case BCTBX_LOG_MESSAGE:
634 			lname = "message";
635 		break;
636 		case BCTBX_LOG_WARNING:
637 			lname = "warning";
638 		break;
639 		case BCTBX_LOG_ERROR:
640 			lname = "error";
641 		break;
642 		case BCTBX_LOG_FATAL:
643 			lname = "fatal";
644 		break;
645 		default:
646 			lname = "badlevel";
647 	}
648 
649 	msg=bctbx_strdup_vprintf(fmt,args);
650 #if defined(_MSC_VER) && !defined(_WIN32_WCE)
651 #ifndef _UNICODE
652 	OutputDebugStringA(msg);
653 	OutputDebugStringA("\r\n");
654 #else
655 	{
656 		size_t len=strlen(msg);
657 		wchar_t *tmp=(wchar_t*)bctbx_malloc0((len+1)*sizeof(wchar_t));
658 		mbstowcs(tmp,msg,len);
659 		OutputDebugStringW(tmp);
660 		OutputDebugStringW(L"\r\n");
661 		bctbx_free(tmp);
662 	}
663 #endif
664 #endif
665 	ret = fprintf(f,"%i-%.2i-%.2i %.2i:%.2i:%.2i:%.3i %s-%s-%s" ENDLINE
666 			,1900+lt->tm_year,1+lt->tm_mon,lt->tm_mday,lt->tm_hour,lt->tm_min,lt->tm_sec
667 		,(int)(tp.tv_usec/1000), (domain?domain:"bctoolbox"), lname, msg);
668 	fflush(f);
669 	if (filehandler->max_size > 0 && ret > 0) {
670 		filehandler->size += ret;
671 		if (filehandler->size > filehandler->max_size) {
672 			_close_log_collection_file(filehandler);
673 			_open_log_collection_file(filehandler);
674 		}
675 	}
676 	bctbx_mutex_unlock(&__bctbx_logger.log_mutex);
677 
678 	bctbx_free(msg);
679 }
680 
bctbx_logv_file_destroy(bctbx_log_handler_t * handler)681 void bctbx_logv_file_destroy(bctbx_log_handler_t* handler) {
682 	bctbx_file_log_handler_t *filehandler = (bctbx_file_log_handler_t *) handler->user_info;
683 	fclose(filehandler->file);
684 	bctbx_free(filehandler->path);
685 	bctbx_free(filehandler->name);
686 	bctbx_logv_out_destroy(handler);
687 }
688 
689 #ifdef __QNX__
690 #include <slog2.h>
691 
692 static bool_t slog2_registered = FALSE;
693 static slog2_buffer_set_config_t slog2_buffer_config;
694 static slog2_buffer_t slog2_buffer_handle[2];
695 
bctbx_qnx_log_handler(const char * domain,BctbxLogLevel lev,const char * fmt,va_list args)696 void bctbx_qnx_log_handler(const char *domain, BctbxLogLevel lev, const char *fmt, va_list args) {
697 	uint8_t severity = SLOG2_DEBUG1;
698 	uint8_t buffer_idx = 1;
699 	char* msg;
700 
701 	if (slog2_registered != TRUE) {
702 		slog2_buffer_config.buffer_set_name = domain;
703 		slog2_buffer_config.num_buffers = 2;
704 		slog2_buffer_config.verbosity_level = SLOG2_DEBUG2;
705 		slog2_buffer_config.buffer_config[0].buffer_name = "hi_rate";
706 		slog2_buffer_config.buffer_config[0].num_pages = 6;
707 		slog2_buffer_config.buffer_config[1].buffer_name = "lo_rate";
708 		slog2_buffer_config.buffer_config[1].num_pages = 2;
709 		if (slog2_register(&slog2_buffer_config, slog2_buffer_handle, 0) == 0) {
710 			slog2_registered = TRUE;
711 		} else {
712 			fprintf(stderr, "Error registering slogger2 buffer!\n");
713 			return;
714 		}
715 	}
716 
717 	switch(lev){
718 		case BCTBX_LOG_DEBUG:
719 			severity = SLOG2_DEBUG1;
720 		break;
721 		case BCTBX_LOG_MESSAGE:
722 			severity = SLOG2_INFO;
723 		break;
724 		case BCTBX_LOG_WARNING:
725 			severity = SLOG2_WARNING;
726 		break;
727 		case BCTBX_LOG_ERROR:
728 			severity = SLOG2_ERROR;
729 		break;
730 		case BCTBX_LOG_FATAL:
731 			severity = SLOG2_CRITICAL;
732 		break;
733 		default:
734 			severity = SLOG2_CRITICAL;
735 	}
736 
737 	msg = bctbx_strdup_vprintf(fmt,args);
738 	slog2c(slog2_buffer_handle[buffer_idx], 0, severity, msg);
739 }
740 
741 #endif /* __QNX__ */
742