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