1 /**
2  * WinPR: Windows Portable Runtime
3  * WinPR Logger
4  *
5  * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <stdio.h>
25 #include <string.h>
26 
27 #include <winpr/crt.h>
28 #include <winpr/print.h>
29 #include <winpr/debug.h>
30 #include <winpr/environment.h>
31 #include <winpr/wlog.h>
32 
33 #if defined(ANDROID)
34 #include <android/log.h>
35 #include "../log.h"
36 #endif
37 
38 #include "wlog.h"
39 
40 struct _wLogFilter
41 {
42 	DWORD Level;
43 	LPSTR* Names;
44 	DWORD NameCount;
45 };
46 typedef struct _wLogFilter wLogFilter;
47 
48 #define WLOG_FILTER_NOT_FILTERED -1
49 #define WLOG_FILTER_NOT_INITIALIZED -2
50 /**
51  * References for general logging concepts:
52  *
53  * Short introduction to log4j:
54  * http://logging.apache.org/log4j/1.2/manual.html
55  *
56  * logging - Logging facility for Python:
57  * http://docs.python.org/2/library/logging.html
58  */
59 
60 LPCSTR WLOG_LEVELS[7] = { "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "OFF" };
61 
62 static INIT_ONCE _WLogInitialized = INIT_ONCE_STATIC_INIT;
63 static DWORD g_FilterCount = 0;
64 static wLogFilter* g_Filters = NULL;
65 static wLog* g_RootLog = NULL;
66 
67 static wLog* WLog_New(LPCSTR name, wLog* rootLogger);
68 static void WLog_Free(wLog* log);
69 static LONG WLog_GetFilterLogLevel(wLog* log);
70 static int WLog_ParseLogLevel(LPCSTR level);
71 static BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name);
72 static BOOL WLog_ParseFilters(wLog* root);
73 static wLog* WLog_Get_int(wLog* root, LPCSTR name);
74 
75 #if !defined(_WIN32)
76 static void WLog_Uninit_(void) __attribute__((destructor));
77 #endif
78 
WLog_Uninit_(void)79 static void WLog_Uninit_(void)
80 {
81 	DWORD index;
82 	wLog* child = NULL;
83 	wLog* root = g_RootLog;
84 
85 	if (!root)
86 		return;
87 
88 	for (index = 0; index < root->ChildrenCount; index++)
89 	{
90 		child = root->Children[index];
91 		WLog_Free(child);
92 	}
93 
94 	WLog_Free(root);
95 	g_RootLog = NULL;
96 }
97 
WLog_InitializeRoot(PINIT_ONCE InitOnce,PVOID Parameter,PVOID * Context)98 static BOOL CALLBACK WLog_InitializeRoot(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context)
99 {
100 	char* env;
101 	DWORD nSize;
102 	DWORD logAppenderType;
103 	LPCSTR appender = "WLOG_APPENDER";
104 
105 	if (!(g_RootLog = WLog_New("", NULL)))
106 		return FALSE;
107 
108 	g_RootLog->IsRoot = TRUE;
109 	logAppenderType = WLOG_APPENDER_CONSOLE;
110 	nSize = GetEnvironmentVariableA(appender, NULL, 0);
111 
112 	if (nSize)
113 	{
114 		env = (LPSTR)malloc(nSize);
115 
116 		if (!env)
117 			goto fail;
118 
119 		if (GetEnvironmentVariableA(appender, env, nSize) != nSize - 1)
120 		{
121 			fprintf(stderr, "%s environment variable modified in my back", appender);
122 			free(env);
123 			goto fail;
124 		}
125 
126 		if (_stricmp(env, "CONSOLE") == 0)
127 			logAppenderType = WLOG_APPENDER_CONSOLE;
128 		else if (_stricmp(env, "FILE") == 0)
129 			logAppenderType = WLOG_APPENDER_FILE;
130 		else if (_stricmp(env, "BINARY") == 0)
131 			logAppenderType = WLOG_APPENDER_BINARY;
132 
133 #ifdef HAVE_SYSLOG_H
134 		else if (_stricmp(env, "SYSLOG") == 0)
135 			logAppenderType = WLOG_APPENDER_SYSLOG;
136 
137 #endif /* HAVE_SYSLOG_H */
138 #ifdef HAVE_JOURNALD_H
139 		else if (_stricmp(env, "JOURNALD") == 0)
140 			logAppenderType = WLOG_APPENDER_JOURNALD;
141 
142 #endif
143 		else if (_stricmp(env, "UDP") == 0)
144 			logAppenderType = WLOG_APPENDER_UDP;
145 
146 		free(env);
147 	}
148 
149 	if (!WLog_SetLogAppenderType(g_RootLog, logAppenderType))
150 		goto fail;
151 
152 	if (!WLog_ParseFilters(g_RootLog))
153 		goto fail;
154 
155 #if defined(_WIN32)
156 	atexit(WLog_Uninit_);
157 #endif
158 	return TRUE;
159 fail:
160 	WLog_Uninit_();
161 	return FALSE;
162 }
163 
log_recursion(LPCSTR file,LPCSTR fkt,int line)164 static BOOL log_recursion(LPCSTR file, LPCSTR fkt, int line)
165 {
166 	BOOL status = FALSE;
167 	char** msg = NULL;
168 	size_t used, i;
169 	void* bt = winpr_backtrace(20);
170 #if defined(ANDROID)
171 	LPCSTR tag = WINPR_TAG("utils.wlog");
172 #endif
173 
174 	if (!bt)
175 		return FALSE;
176 
177 	msg = winpr_backtrace_symbols(bt, &used);
178 
179 	if (!msg)
180 		goto out;
181 
182 #if defined(ANDROID)
183 
184 	if (__android_log_print(ANDROID_LOG_FATAL, tag, "Recursion detected!!!") < 0)
185 		goto out;
186 
187 	if (__android_log_print(ANDROID_LOG_FATAL, tag, "Check %s [%s:%d]", fkt, file, line) < 0)
188 		goto out;
189 
190 	for (i = 0; i < used; i++)
191 		if (__android_log_print(ANDROID_LOG_FATAL, tag, "%zd: %s", i, msg[i]) < 0)
192 			goto out;
193 
194 #else
195 
196 	if (fprintf(stderr, "[%s]: Recursion detected!\n", fkt) < 0)
197 		goto out;
198 
199 	if (fprintf(stderr, "[%s]: Check %s:%d\n", fkt, file, line) < 0)
200 		goto out;
201 
202 	for (i = 0; i < used; i++)
203 		if (fprintf(stderr, "%s: %" PRIuz ": %s\n", fkt, i, msg[i]) < 0)
204 			goto out;
205 
206 #endif
207 	status = TRUE;
208 out:
209 	free(msg);
210 	winpr_backtrace_free(bt);
211 	return status;
212 }
213 
WLog_Write(wLog * log,wLogMessage * message)214 static BOOL WLog_Write(wLog* log, wLogMessage* message)
215 {
216 	BOOL status;
217 	wLogAppender* appender;
218 	appender = WLog_GetLogAppender(log);
219 
220 	if (!appender)
221 		return FALSE;
222 
223 	if (!appender->active)
224 		if (!WLog_OpenAppender(log))
225 			return FALSE;
226 
227 	if (!appender->WriteMessage)
228 		return FALSE;
229 
230 	EnterCriticalSection(&appender->lock);
231 
232 	if (appender->recursive)
233 		status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
234 	else
235 	{
236 		appender->recursive = TRUE;
237 		status = appender->WriteMessage(log, appender, message);
238 		appender->recursive = FALSE;
239 	}
240 
241 	LeaveCriticalSection(&appender->lock);
242 	return status;
243 }
244 
WLog_WriteData(wLog * log,wLogMessage * message)245 static BOOL WLog_WriteData(wLog* log, wLogMessage* message)
246 {
247 	BOOL status;
248 	wLogAppender* appender;
249 	appender = WLog_GetLogAppender(log);
250 
251 	if (!appender)
252 		return FALSE;
253 
254 	if (!appender->active)
255 		if (!WLog_OpenAppender(log))
256 			return FALSE;
257 
258 	if (!appender->WriteDataMessage)
259 		return FALSE;
260 
261 	EnterCriticalSection(&appender->lock);
262 
263 	if (appender->recursive)
264 		status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
265 	else
266 	{
267 		appender->recursive = TRUE;
268 		status = appender->WriteDataMessage(log, appender, message);
269 		appender->recursive = FALSE;
270 	}
271 
272 	LeaveCriticalSection(&appender->lock);
273 	return status;
274 }
275 
WLog_WriteImage(wLog * log,wLogMessage * message)276 static BOOL WLog_WriteImage(wLog* log, wLogMessage* message)
277 {
278 	BOOL status;
279 	wLogAppender* appender;
280 	appender = WLog_GetLogAppender(log);
281 
282 	if (!appender)
283 		return FALSE;
284 
285 	if (!appender->active)
286 		if (!WLog_OpenAppender(log))
287 			return FALSE;
288 
289 	if (!appender->WriteImageMessage)
290 		return FALSE;
291 
292 	EnterCriticalSection(&appender->lock);
293 
294 	if (appender->recursive)
295 		status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
296 	else
297 	{
298 		appender->recursive = TRUE;
299 		status = appender->WriteImageMessage(log, appender, message);
300 		appender->recursive = FALSE;
301 	}
302 
303 	LeaveCriticalSection(&appender->lock);
304 	return status;
305 }
306 
WLog_WritePacket(wLog * log,wLogMessage * message)307 static BOOL WLog_WritePacket(wLog* log, wLogMessage* message)
308 {
309 	BOOL status;
310 	wLogAppender* appender;
311 	appender = WLog_GetLogAppender(log);
312 
313 	if (!appender)
314 		return FALSE;
315 
316 	if (!appender->active)
317 		if (!WLog_OpenAppender(log))
318 			return FALSE;
319 
320 	if (!appender->WritePacketMessage)
321 		return FALSE;
322 
323 	EnterCriticalSection(&appender->lock);
324 
325 	if (appender->recursive)
326 		status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
327 	else
328 	{
329 		appender->recursive = TRUE;
330 		status = appender->WritePacketMessage(log, appender, message);
331 		appender->recursive = FALSE;
332 	}
333 
334 	LeaveCriticalSection(&appender->lock);
335 	return status;
336 }
337 
WLog_PrintMessageVA(wLog * log,DWORD type,DWORD level,DWORD line,const char * file,const char * function,va_list args)338 BOOL WLog_PrintMessageVA(wLog* log, DWORD type, DWORD level, DWORD line, const char* file,
339                          const char* function, va_list args)
340 {
341 	BOOL status = FALSE;
342 	wLogMessage message = { 0 };
343 	message.Type = type;
344 	message.Level = level;
345 	message.LineNumber = line;
346 	message.FileName = file;
347 	message.FunctionName = function;
348 
349 	switch (type)
350 	{
351 		case WLOG_MESSAGE_TEXT:
352 			message.FormatString = va_arg(args, const char*);
353 
354 			if (!strchr(message.FormatString, '%'))
355 			{
356 				message.TextString = (LPSTR)message.FormatString;
357 				status = WLog_Write(log, &message);
358 			}
359 			else
360 			{
361 				char formattedLogMessage[WLOG_MAX_STRING_SIZE];
362 
363 				if (wvsnprintfx(formattedLogMessage, WLOG_MAX_STRING_SIZE - 1, message.FormatString,
364 				                args) < 0)
365 					return FALSE;
366 
367 				message.TextString = formattedLogMessage;
368 				status = WLog_Write(log, &message);
369 			}
370 
371 			break;
372 
373 		case WLOG_MESSAGE_DATA:
374 			message.Data = va_arg(args, void*);
375 			message.Length = va_arg(args, int);
376 			status = WLog_WriteData(log, &message);
377 			break;
378 
379 		case WLOG_MESSAGE_IMAGE:
380 			message.ImageData = va_arg(args, void*);
381 			message.ImageWidth = va_arg(args, int);
382 			message.ImageHeight = va_arg(args, int);
383 			message.ImageBpp = va_arg(args, int);
384 			status = WLog_WriteImage(log, &message);
385 			break;
386 
387 		case WLOG_MESSAGE_PACKET:
388 			message.PacketData = va_arg(args, void*);
389 			message.PacketLength = va_arg(args, int);
390 			message.PacketFlags = va_arg(args, int);
391 			status = WLog_WritePacket(log, &message);
392 			break;
393 
394 		default:
395 			break;
396 	}
397 
398 	return status;
399 }
400 
WLog_PrintMessage(wLog * log,DWORD type,DWORD level,DWORD line,const char * file,const char * function,...)401 BOOL WLog_PrintMessage(wLog* log, DWORD type, DWORD level, DWORD line, const char* file,
402                        const char* function, ...)
403 {
404 	BOOL status;
405 	va_list args;
406 	va_start(args, function);
407 	status = WLog_PrintMessageVA(log, type, level, line, file, function, args);
408 	va_end(args);
409 	return status;
410 }
411 
WLog_GetLogLevel(wLog * log)412 DWORD WLog_GetLogLevel(wLog* log)
413 {
414 	if (!log)
415 		return WLOG_OFF;
416 
417 	if (log->FilterLevel <= WLOG_FILTER_NOT_INITIALIZED)
418 		log->FilterLevel = WLog_GetFilterLogLevel(log);
419 
420 	if (log->FilterLevel > WLOG_FILTER_NOT_FILTERED)
421 		return (DWORD)log->FilterLevel;
422 	else if (log->Level == WLOG_LEVEL_INHERIT)
423 		log->Level = WLog_GetLogLevel(log->Parent);
424 
425 	return log->Level;
426 }
427 
WLog_IsLevelActive(wLog * _log,DWORD _log_level)428 BOOL WLog_IsLevelActive(wLog* _log, DWORD _log_level)
429 {
430 	DWORD level;
431 
432 	if (!_log)
433 		return FALSE;
434 
435 	level = WLog_GetLogLevel(_log);
436 
437 	if (level == WLOG_OFF)
438 		return FALSE;
439 
440 	return _log_level >= level;
441 }
442 
WLog_SetStringLogLevel(wLog * log,LPCSTR level)443 BOOL WLog_SetStringLogLevel(wLog* log, LPCSTR level)
444 {
445 	int lvl;
446 
447 	if (!log || !level)
448 		return FALSE;
449 
450 	lvl = WLog_ParseLogLevel(level);
451 
452 	if (lvl < 0)
453 		return FALSE;
454 
455 	return WLog_SetLogLevel(log, (DWORD)lvl);
456 }
457 
WLog_reset_log_filters(wLog * log)458 static BOOL WLog_reset_log_filters(wLog* log)
459 {
460 	DWORD x;
461 
462 	if (!log)
463 		return FALSE;
464 
465 	log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED;
466 
467 	for (x = 0; x < log->ChildrenCount; x++)
468 	{
469 		wLog* child = log->Children[x];
470 
471 		if (!WLog_reset_log_filters(child))
472 			return FALSE;
473 	}
474 
475 	return TRUE;
476 }
477 
WLog_AddStringLogFilters_int(wLog * root,LPCSTR filter)478 static BOOL WLog_AddStringLogFilters_int(wLog* root, LPCSTR filter)
479 {
480 	DWORD pos;
481 	DWORD size;
482 	DWORD count;
483 	LPSTR p;
484 	LPSTR filterStr;
485 	LPSTR cp;
486 	wLogFilter* tmp;
487 
488 	if (!filter)
489 		return FALSE;
490 
491 	count = 1;
492 	p = (LPSTR)filter;
493 
494 	while ((p = strchr(p, ',')) != NULL)
495 	{
496 		count++;
497 		p++;
498 	}
499 
500 	pos = g_FilterCount;
501 	size = g_FilterCount + count;
502 	tmp = (wLogFilter*)realloc(g_Filters, size * sizeof(wLogFilter));
503 
504 	if (!tmp)
505 		return FALSE;
506 
507 	g_Filters = tmp;
508 	cp = (LPSTR)_strdup(filter);
509 
510 	if (!cp)
511 		return FALSE;
512 
513 	p = cp;
514 	filterStr = cp;
515 
516 	do
517 	{
518 		p = strchr(p, ',');
519 
520 		if (p)
521 			*p = '\0';
522 
523 		if (pos < size)
524 		{
525 			if (!WLog_ParseFilter(root, &g_Filters[pos++], filterStr))
526 			{
527 				free(cp);
528 				return FALSE;
529 			}
530 		}
531 		else
532 			break;
533 
534 		if (p)
535 		{
536 			filterStr = p + 1;
537 			p++;
538 		}
539 	} while (p != NULL);
540 
541 	g_FilterCount = size;
542 	free(cp);
543 	return WLog_reset_log_filters(root);
544 }
545 
WLog_AddStringLogFilters(LPCSTR filter)546 BOOL WLog_AddStringLogFilters(LPCSTR filter)
547 {
548 	/* Ensure logger is initialized */
549 	wLog* root = WLog_GetRoot();
550 	return WLog_AddStringLogFilters_int(root, filter);
551 }
552 
WLog_UpdateInheritLevel(wLog * log,DWORD logLevel)553 static BOOL WLog_UpdateInheritLevel(wLog* log, DWORD logLevel)
554 {
555 	if (!log)
556 		return FALSE;
557 
558 	if (log->inherit)
559 	{
560 		DWORD x;
561 		log->Level = logLevel;
562 
563 		for (x = 0; x < log->ChildrenCount; x++)
564 		{
565 			wLog* child = log->Children[x];
566 
567 			if (!WLog_UpdateInheritLevel(child, logLevel))
568 				return FALSE;
569 		}
570 	}
571 
572 	return TRUE;
573 }
574 
WLog_SetLogLevel(wLog * log,DWORD logLevel)575 BOOL WLog_SetLogLevel(wLog* log, DWORD logLevel)
576 {
577 	DWORD x;
578 
579 	if (!log)
580 		return FALSE;
581 
582 	if ((logLevel > WLOG_OFF) && (logLevel != WLOG_LEVEL_INHERIT))
583 		logLevel = WLOG_OFF;
584 
585 	log->Level = logLevel;
586 	log->inherit = (logLevel == WLOG_LEVEL_INHERIT) ? TRUE : FALSE;
587 
588 	for (x = 0; x < log->ChildrenCount; x++)
589 	{
590 		wLog* child = log->Children[x];
591 
592 		if (!WLog_UpdateInheritLevel(child, logLevel))
593 			return FALSE;
594 	}
595 
596 	return WLog_reset_log_filters(log);
597 }
598 
WLog_ParseLogLevel(LPCSTR level)599 int WLog_ParseLogLevel(LPCSTR level)
600 {
601 	int iLevel = -1;
602 
603 	if (!level)
604 		return -1;
605 
606 	if (_stricmp(level, "TRACE") == 0)
607 		iLevel = WLOG_TRACE;
608 	else if (_stricmp(level, "DEBUG") == 0)
609 		iLevel = WLOG_DEBUG;
610 	else if (_stricmp(level, "INFO") == 0)
611 		iLevel = WLOG_INFO;
612 	else if (_stricmp(level, "WARN") == 0)
613 		iLevel = WLOG_WARN;
614 	else if (_stricmp(level, "ERROR") == 0)
615 		iLevel = WLOG_ERROR;
616 	else if (_stricmp(level, "FATAL") == 0)
617 		iLevel = WLOG_FATAL;
618 	else if (_stricmp(level, "OFF") == 0)
619 		iLevel = WLOG_OFF;
620 
621 	return iLevel;
622 }
623 
WLog_ParseFilter(wLog * root,wLogFilter * filter,LPCSTR name)624 BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name)
625 {
626 	char* p;
627 	char* q;
628 	int count;
629 	LPSTR names;
630 	int iLevel;
631 	count = 1;
632 
633 	if (!name)
634 		return FALSE;
635 
636 	p = (char*)name;
637 
638 	if (p)
639 	{
640 		while ((p = strchr(p, '.')) != NULL)
641 		{
642 			count++;
643 			p++;
644 		}
645 	}
646 
647 	names = _strdup(name);
648 
649 	if (!names)
650 		return FALSE;
651 
652 	filter->NameCount = count;
653 	filter->Names = (LPSTR*)calloc((count + 1UL), sizeof(LPSTR));
654 
655 	if (!filter->Names)
656 	{
657 		free(names);
658 		filter->NameCount = 0;
659 		return FALSE;
660 	}
661 
662 	filter->Names[count] = NULL;
663 	count = 0;
664 	p = (char*)names;
665 	filter->Names[count++] = p;
666 	q = strrchr(p, ':');
667 
668 	if (!q)
669 	{
670 		free(names);
671 		free(filter->Names);
672 		filter->Names = NULL;
673 		filter->NameCount = 0;
674 		return FALSE;
675 	}
676 
677 	*q = '\0';
678 	q++;
679 	iLevel = WLog_ParseLogLevel(q);
680 
681 	if (iLevel < 0)
682 	{
683 		free(names);
684 		free(filter->Names);
685 		filter->Names = NULL;
686 		filter->NameCount = 0;
687 		return FALSE;
688 	}
689 
690 	filter->Level = (DWORD)iLevel;
691 
692 	while ((p = strchr(p, '.')) != NULL)
693 	{
694 		if (count < (int)filter->NameCount)
695 			filter->Names[count++] = p + 1;
696 
697 		*p = '\0';
698 		p++;
699 	}
700 
701 	return TRUE;
702 }
703 
WLog_ParseFilters(wLog * root)704 BOOL WLog_ParseFilters(wLog* root)
705 {
706 	LPCSTR filter = "WLOG_FILTER";
707 	BOOL res = FALSE;
708 	char* env;
709 	DWORD nSize;
710 	free(g_Filters);
711 	g_Filters = NULL;
712 	g_FilterCount = 0;
713 	nSize = GetEnvironmentVariableA(filter, NULL, 0);
714 
715 	if (nSize < 1)
716 		return TRUE;
717 
718 	env = (LPSTR)malloc(nSize);
719 
720 	if (!env)
721 		return FALSE;
722 
723 	if (GetEnvironmentVariableA(filter, env, nSize) == nSize - 1)
724 		res = WLog_AddStringLogFilters_int(root, env);
725 
726 	free(env);
727 	return res;
728 }
729 
WLog_GetFilterLogLevel(wLog * log)730 LONG WLog_GetFilterLogLevel(wLog* log)
731 {
732 	DWORD i, j;
733 	BOOL match = FALSE;
734 
735 	if (log->FilterLevel >= 0)
736 		return log->FilterLevel;
737 
738 	for (i = 0; i < g_FilterCount; i++)
739 	{
740 		for (j = 0; j < g_Filters[i].NameCount; j++)
741 		{
742 			if (j >= log->NameCount)
743 				break;
744 
745 			if (_stricmp(g_Filters[i].Names[j], "*") == 0)
746 			{
747 				match = TRUE;
748 				break;
749 			}
750 
751 			if (_stricmp(g_Filters[i].Names[j], log->Names[j]) != 0)
752 				break;
753 
754 			if (j == (log->NameCount - 1))
755 			{
756 				match = TRUE;
757 				break;
758 			}
759 		}
760 
761 		if (match)
762 			break;
763 	}
764 
765 	if (match)
766 		log->FilterLevel = g_Filters[i].Level;
767 	else
768 		log->FilterLevel = WLOG_FILTER_NOT_FILTERED;
769 
770 	return log->FilterLevel;
771 }
772 
WLog_ParseName(wLog * log,LPCSTR name)773 static BOOL WLog_ParseName(wLog* log, LPCSTR name)
774 {
775 	char* p;
776 	int count;
777 	LPSTR names;
778 	count = 1;
779 	p = (char*)name;
780 
781 	while ((p = strchr(p, '.')) != NULL)
782 	{
783 		count++;
784 		p++;
785 	}
786 
787 	names = _strdup(name);
788 
789 	if (!names)
790 		return FALSE;
791 
792 	log->NameCount = count;
793 	log->Names = (LPSTR*)calloc((count + 1UL), sizeof(LPSTR));
794 
795 	if (!log->Names)
796 	{
797 		free(names);
798 		return FALSE;
799 	}
800 
801 	log->Names[count] = NULL;
802 	count = 0;
803 	p = (char*)names;
804 	log->Names[count++] = p;
805 
806 	while ((p = strchr(p, '.')) != NULL)
807 	{
808 		if (count < (int)log->NameCount)
809 			log->Names[count++] = p + 1;
810 
811 		*p = '\0';
812 		p++;
813 	}
814 
815 	return TRUE;
816 }
817 
WLog_New(LPCSTR name,wLog * rootLogger)818 wLog* WLog_New(LPCSTR name, wLog* rootLogger)
819 {
820 	wLog* log = NULL;
821 	char* env = NULL;
822 	DWORD nSize;
823 	int iLevel;
824 	log = (wLog*)calloc(1, sizeof(wLog));
825 
826 	if (!log)
827 		return NULL;
828 
829 	log->Name = _strdup(name);
830 
831 	if (!log->Name)
832 		goto out_fail;
833 
834 	if (!WLog_ParseName(log, name))
835 		goto out_fail;
836 
837 	log->Parent = rootLogger;
838 	log->ChildrenCount = 0;
839 	log->ChildrenSize = 16;
840 	log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED;
841 
842 	if (!(log->Children = (wLog**)calloc(log->ChildrenSize, sizeof(wLog*))))
843 		goto out_fail;
844 
845 	log->Appender = NULL;
846 
847 	if (rootLogger)
848 	{
849 		log->Level = WLOG_LEVEL_INHERIT;
850 		log->inherit = TRUE;
851 	}
852 	else
853 	{
854 		LPCSTR level = "WLOG_LEVEL";
855 		log->Level = WLOG_INFO;
856 		nSize = GetEnvironmentVariableA(level, NULL, 0);
857 
858 		if (nSize)
859 		{
860 			env = (LPSTR)malloc(nSize);
861 
862 			if (!env)
863 				goto out_fail;
864 
865 			if (GetEnvironmentVariableA(level, env, nSize) != nSize - 1)
866 			{
867 				fprintf(stderr, "%s environment variable changed in my back !\n", level);
868 				free(env);
869 				goto out_fail;
870 			}
871 
872 			iLevel = WLog_ParseLogLevel(env);
873 			free(env);
874 
875 			if (iLevel >= 0)
876 			{
877 				if (!WLog_SetLogLevel(log, (DWORD)iLevel))
878 					goto out_fail;
879 			}
880 		}
881 	}
882 
883 	iLevel = WLog_GetFilterLogLevel(log);
884 
885 	if (iLevel >= 0)
886 	{
887 		if (!WLog_SetLogLevel(log, (DWORD)iLevel))
888 			goto out_fail;
889 	}
890 
891 	return log;
892 out_fail:
893 	free(log->Children);
894 	free(log->Name);
895 	free(log);
896 	return NULL;
897 }
898 
WLog_Free(wLog * log)899 void WLog_Free(wLog* log)
900 {
901 	if (log)
902 	{
903 		if (log->Appender)
904 		{
905 			WLog_Appender_Free(log, log->Appender);
906 			log->Appender = NULL;
907 		}
908 
909 		free(log->Name);
910 		free(log->Names[0]);
911 		free(log->Names);
912 		free(log->Children);
913 		free(log);
914 	}
915 }
916 
WLog_GetRoot(void)917 wLog* WLog_GetRoot(void)
918 {
919 	if (!InitOnceExecuteOnce(&_WLogInitialized, WLog_InitializeRoot, NULL, NULL))
920 		return NULL;
921 
922 	return g_RootLog;
923 }
924 
WLog_AddChild(wLog * parent,wLog * child)925 static BOOL WLog_AddChild(wLog* parent, wLog* child)
926 {
927 	if (parent->ChildrenCount >= parent->ChildrenSize)
928 	{
929 		wLog** tmp;
930 		parent->ChildrenSize *= 2;
931 
932 		if (!parent->ChildrenSize)
933 		{
934 			if (parent->Children)
935 				free(parent->Children);
936 
937 			parent->Children = NULL;
938 		}
939 		else
940 		{
941 			tmp = (wLog**)realloc(parent->Children, sizeof(wLog*) * parent->ChildrenSize);
942 
943 			if (!tmp)
944 			{
945 				if (parent->Children)
946 					free(parent->Children);
947 
948 				parent->Children = NULL;
949 				return FALSE;
950 			}
951 
952 			parent->Children = tmp;
953 		}
954 	}
955 
956 	if (!parent->Children)
957 		return FALSE;
958 
959 	parent->Children[parent->ChildrenCount++] = child;
960 	child->Parent = parent;
961 	return TRUE;
962 }
963 
WLog_FindChild(wLog * root,LPCSTR name)964 static wLog* WLog_FindChild(wLog* root, LPCSTR name)
965 {
966 	DWORD index;
967 	wLog* child = NULL;
968 	BOOL found = FALSE;
969 
970 	if (!root)
971 		return NULL;
972 
973 	for (index = 0; index < root->ChildrenCount; index++)
974 	{
975 		child = root->Children[index];
976 
977 		if (strcmp(child->Name, name) == 0)
978 		{
979 			found = TRUE;
980 			break;
981 		}
982 	}
983 
984 	return (found) ? child : NULL;
985 }
986 
WLog_Get_int(wLog * root,LPCSTR name)987 static wLog* WLog_Get_int(wLog* root, LPCSTR name)
988 {
989 	wLog* log;
990 
991 	if (!(log = WLog_FindChild(root, name)))
992 	{
993 		if (!root)
994 			return NULL;
995 
996 		if (!(log = WLog_New(name, root)))
997 			return NULL;
998 
999 		if (!WLog_AddChild(root, log))
1000 		{
1001 			WLog_Free(log);
1002 			return NULL;
1003 		}
1004 	}
1005 
1006 	return log;
1007 }
1008 
WLog_Get(LPCSTR name)1009 wLog* WLog_Get(LPCSTR name)
1010 {
1011 	wLog* root = WLog_GetRoot();
1012 	return WLog_Get_int(root, name);
1013 }
1014 
WLog_Init(void)1015 BOOL WLog_Init(void)
1016 {
1017 	return WLog_GetRoot() != NULL;
1018 }
1019 
WLog_Uninit(void)1020 BOOL WLog_Uninit(void)
1021 {
1022 	return TRUE;
1023 }
1024