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