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