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