FreeRDP
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 typedef 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)
60 LPCSTR WLOG_LEVELS[7] = { "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "OFF" };
61 
62 static INIT_ONCE g_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 
79 static 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 
97 static void WLog_Lock(wLog* log)
98 {
99  WINPR_ASSERT(log);
100  EnterCriticalSection(&log->lock);
101 }
102 
103 static void WLog_Unlock(wLog* log)
104 {
105  WINPR_ASSERT(log);
106  LeaveCriticalSection(&log->lock);
107 }
108 
109 static 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;
173 fail:
174  WLog_Uninit_();
175  return FALSE;
176 }
177 
178 static 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;
222 out:
223  free(msg);
224  winpr_backtrace_free(bt);
225  return status;
226 }
227 
228 static 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 
259 static 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 
290 static 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 
321 static 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 
352 BOOL 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 
418 BOOL 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 
429 DWORD 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 
445 BOOL 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 
460 BOOL 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 
475 static 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 
493 static 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 
556 BOOL WLog_AddStringLogFilters(LPCSTR filter)
557 {
558  /* Ensure logger is initialized */
559  wLog* root = WLog_GetRoot();
560  return WLog_AddStringLogFilters_int(root, filter);
561 }
562 
563 static 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 
584 BOOL 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 
606 int 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 
631 BOOL 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(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(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 
714 BOOL 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 
740 LONG 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 
786 static 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 
830 wLog* 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;
906 out_fail:
907  free(log->Children);
908  free(log->Name);
909  free(log);
910  return NULL;
911 }
912 
913 void WLog_Free(wLog* log)
914 {
915  if (log)
916  {
917  if (log->Appender)
918  {
919  WLog_Appender_Free(log, log->Appender);
920  log->Appender = NULL;
921  }
922 
923  free(log->Name);
924  free(log->Names[0]);
925  free(log->Names);
926  free(log->Children);
927  DeleteCriticalSection(&log->lock);
928  free(log);
929  }
930 }
931 
932 wLog* WLog_GetRoot(void)
933 {
934  if (!InitOnceExecuteOnce(&g_WLogInitialized, WLog_InitializeRoot, NULL, NULL))
935  return NULL;
936 
937  return g_RootLog;
938 }
939 
940 static BOOL WLog_AddChild(wLog* parent, wLog* child)
941 {
942  BOOL status = FALSE;
943 
944  WLog_Lock(parent);
945 
946  if (parent->ChildrenCount >= parent->ChildrenSize)
947  {
948  wLog** tmp = NULL;
949  parent->ChildrenSize *= 2;
950 
951  if (!parent->ChildrenSize)
952  {
953  if (parent->Children)
954  free(parent->Children);
955 
956  parent->Children = NULL;
957  }
958  else
959  {
960  tmp = (wLog**)realloc(parent->Children, sizeof(wLog*) * parent->ChildrenSize);
961 
962  if (!tmp)
963  {
964  if (parent->Children)
965  free(parent->Children);
966 
967  parent->Children = NULL;
968  goto exit;
969  }
970 
971  parent->Children = tmp;
972  }
973  }
974 
975  if (!parent->Children)
976  goto exit;
977 
978  parent->Children[parent->ChildrenCount++] = child;
979  child->Parent = parent;
980 
981  WLog_Unlock(parent);
982 
983  status = TRUE;
984 exit:
985  return status;
986 }
987 
988 static wLog* WLog_FindChild(wLog* root, LPCSTR name)
989 {
990  wLog* child = NULL;
991  BOOL found = FALSE;
992 
993  if (!root)
994  return NULL;
995 
996  WLog_Lock(root);
997 
998  for (DWORD index = 0; index < root->ChildrenCount; index++)
999  {
1000  child = root->Children[index];
1001 
1002  if (strcmp(child->Name, name) == 0)
1003  {
1004  found = TRUE;
1005  break;
1006  }
1007  }
1008 
1009  WLog_Unlock(root);
1010 
1011  return (found) ? child : NULL;
1012 }
1013 
1014 static wLog* WLog_Get_int(wLog* root, LPCSTR name)
1015 {
1016  wLog* log = NULL;
1017 
1018  if (!(log = WLog_FindChild(root, name)))
1019  {
1020  if (!root)
1021  return NULL;
1022 
1023  if (!(log = WLog_New(name, root)))
1024  return NULL;
1025 
1026  if (!WLog_AddChild(root, log))
1027  {
1028  WLog_Free(log);
1029  return NULL;
1030  }
1031  }
1032 
1033  return log;
1034 }
1035 
1036 wLog* WLog_Get(LPCSTR name)
1037 {
1038  wLog* root = WLog_GetRoot();
1039  return WLog_Get_int(root, name);
1040 }
1041 
1042 #if defined(WITH_WINPR_DEPRECATED)
1043 BOOL WLog_Init(void)
1044 {
1045  return WLog_GetRoot() != NULL;
1046 }
1047 
1048 BOOL WLog_Uninit(void)
1049 {
1050  wLog* root = g_RootLog;
1051 
1052  if (!root)
1053  return FALSE;
1054 
1055  WLog_Lock(root);
1056 
1057  for (DWORD index = 0; index < root->ChildrenCount; index++)
1058  {
1059  wLog* child = root->Children[index];
1060  WLog_Free(child);
1061  }
1062 
1063  WLog_Unlock(root);
1064 
1065  WLog_Free(root);
1066  g_RootLog = NULL;
1067 
1068  return TRUE;
1069 }
1070 #endif
1071 
1072 BOOL WLog_SetContext(wLog* log, const char* (*fkt)(void*), void* context)
1073 {
1074  WINPR_ASSERT(log);
1075 
1076  log->custom = fkt;
1077  log->context = context;
1078  return TRUE;
1079 }