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