summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/utils/wlog/wlog.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /winpr/libwinpr/utils/wlog/wlog.c
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'winpr/libwinpr/utils/wlog/wlog.c')
-rw-r--r--winpr/libwinpr/utils/wlog/wlog.c1071
1 files changed, 1071 insertions, 0 deletions
diff --git a/winpr/libwinpr/utils/wlog/wlog.c b/winpr/libwinpr/utils/wlog/wlog.c
new file mode 100644
index 0000000..4f064ff
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/wlog.c
@@ -0,0 +1,1071 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * WinPR Logger
+ *
+ * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/print.h>
+#include <winpr/debug.h>
+#include <winpr/environment.h>
+#include <winpr/wlog.h>
+
+#if defined(ANDROID)
+#include <android/log.h>
+#include "../log.h"
+#endif
+
+#include "wlog.h"
+
+typedef struct
+{
+ DWORD Level;
+ LPSTR* Names;
+ size_t NameCount;
+} wLogFilter;
+
+#define WLOG_FILTER_NOT_FILTERED -1
+#define WLOG_FILTER_NOT_INITIALIZED -2
+/**
+ * References for general logging concepts:
+ *
+ * Short introduction to log4j:
+ * http://logging.apache.org/log4j/1.2/manual.html
+ *
+ * logging - Logging facility for Python:
+ * http://docs.python.org/2/library/logging.html
+ */
+
+LPCSTR WLOG_LEVELS[7] = { "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "OFF" };
+
+static INIT_ONCE _WLogInitialized = INIT_ONCE_STATIC_INIT;
+static DWORD g_FilterCount = 0;
+static wLogFilter* g_Filters = NULL;
+static wLog* g_RootLog = NULL;
+
+static wLog* WLog_New(LPCSTR name, wLog* rootLogger);
+static void WLog_Free(wLog* log);
+static LONG WLog_GetFilterLogLevel(wLog* log);
+static int WLog_ParseLogLevel(LPCSTR level);
+static BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name);
+static BOOL WLog_ParseFilters(wLog* root);
+static wLog* WLog_Get_int(wLog* root, LPCSTR name);
+
+#if !defined(_WIN32)
+static void WLog_Uninit_(void) __attribute__((destructor));
+#endif
+
+static void WLog_Uninit_(void)
+{
+ wLog* child = NULL;
+ wLog* root = g_RootLog;
+
+ if (!root)
+ return;
+
+ for (DWORD index = 0; index < root->ChildrenCount; index++)
+ {
+ child = root->Children[index];
+ WLog_Free(child);
+ }
+
+ WLog_Free(root);
+ g_RootLog = NULL;
+}
+
+static void WLog_Lock(wLog* log)
+{
+ WINPR_ASSERT(log);
+ EnterCriticalSection(&log->lock);
+}
+
+static void WLog_Unlock(wLog* log)
+{
+ WINPR_ASSERT(log);
+ LeaveCriticalSection(&log->lock);
+}
+
+static BOOL CALLBACK WLog_InitializeRoot(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context)
+{
+ char* env = NULL;
+ DWORD nSize = 0;
+ DWORD logAppenderType = 0;
+ LPCSTR appender = "WLOG_APPENDER";
+
+ WINPR_UNUSED(InitOnce);
+ WINPR_UNUSED(Parameter);
+ WINPR_UNUSED(Context);
+
+ if (!(g_RootLog = WLog_New("", NULL)))
+ return FALSE;
+
+ g_RootLog->IsRoot = TRUE;
+ logAppenderType = WLOG_APPENDER_CONSOLE;
+ nSize = GetEnvironmentVariableA(appender, NULL, 0);
+
+ if (nSize)
+ {
+ env = (LPSTR)malloc(nSize);
+
+ if (!env)
+ goto fail;
+
+ if (GetEnvironmentVariableA(appender, env, nSize) != nSize - 1)
+ {
+ fprintf(stderr, "%s environment variable modified in my back", appender);
+ free(env);
+ goto fail;
+ }
+
+ if (_stricmp(env, "CONSOLE") == 0)
+ logAppenderType = WLOG_APPENDER_CONSOLE;
+ else if (_stricmp(env, "FILE") == 0)
+ logAppenderType = WLOG_APPENDER_FILE;
+ else if (_stricmp(env, "BINARY") == 0)
+ logAppenderType = WLOG_APPENDER_BINARY;
+
+#ifdef WINPR_HAVE_SYSLOG_H
+ else if (_stricmp(env, "SYSLOG") == 0)
+ logAppenderType = WLOG_APPENDER_SYSLOG;
+
+#endif /* WINPR_HAVE_SYSLOG_H */
+#ifdef WINPR_HAVE_JOURNALD_H
+ else if (_stricmp(env, "JOURNALD") == 0)
+ logAppenderType = WLOG_APPENDER_JOURNALD;
+
+#endif
+ else if (_stricmp(env, "UDP") == 0)
+ logAppenderType = WLOG_APPENDER_UDP;
+
+ free(env);
+ }
+
+ if (!WLog_SetLogAppenderType(g_RootLog, logAppenderType))
+ goto fail;
+
+ if (!WLog_ParseFilters(g_RootLog))
+ goto fail;
+
+ atexit(WLog_Uninit_);
+
+ return TRUE;
+fail:
+ WLog_Uninit_();
+ return FALSE;
+}
+
+static BOOL log_recursion(LPCSTR file, LPCSTR fkt, size_t line)
+{
+ BOOL status = FALSE;
+ char** msg = NULL;
+ size_t used = 0;
+ void* bt = winpr_backtrace(20);
+#if defined(ANDROID)
+ LPCSTR tag = WINPR_TAG("utils.wlog");
+#endif
+
+ if (!bt)
+ return FALSE;
+
+ msg = winpr_backtrace_symbols(bt, &used);
+
+ if (!msg)
+ goto out;
+
+#if defined(ANDROID)
+
+ if (__android_log_print(ANDROID_LOG_FATAL, tag, "Recursion detected!!!") < 0)
+ goto out;
+
+ if (__android_log_print(ANDROID_LOG_FATAL, tag, "Check %s [%s:%zu]", fkt, file, line) < 0)
+ goto out;
+
+ for (size_t i = 0; i < used; i++)
+ if (__android_log_print(ANDROID_LOG_FATAL, tag, "%zu: %s", i, msg[i]) < 0)
+ goto out;
+
+#else
+
+ if (fprintf(stderr, "[%s]: Recursion detected!\n", fkt) < 0)
+ goto out;
+
+ if (fprintf(stderr, "[%s]: Check %s:%" PRIuz "\n", fkt, file, line) < 0)
+ goto out;
+
+ for (size_t i = 0; i < used; i++)
+ if (fprintf(stderr, "%s: %" PRIuz ": %s\n", fkt, i, msg[i]) < 0)
+ goto out;
+
+#endif
+ status = TRUE;
+out:
+ free(msg);
+ winpr_backtrace_free(bt);
+ return status;
+}
+
+static BOOL WLog_Write(wLog* log, wLogMessage* message)
+{
+ BOOL status = FALSE;
+ wLogAppender* appender = NULL;
+ appender = WLog_GetLogAppender(log);
+
+ if (!appender)
+ return FALSE;
+
+ if (!appender->active)
+ if (!WLog_OpenAppender(log))
+ return FALSE;
+
+ EnterCriticalSection(&appender->lock);
+
+ if (appender->WriteMessage)
+ {
+ if (appender->recursive)
+ status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
+ else
+ {
+ appender->recursive = TRUE;
+ status = appender->WriteMessage(log, appender, message);
+ appender->recursive = FALSE;
+ }
+ }
+
+ LeaveCriticalSection(&appender->lock);
+ return status;
+}
+
+static BOOL WLog_WriteData(wLog* log, wLogMessage* message)
+{
+ BOOL status = 0;
+ wLogAppender* appender = NULL;
+ appender = WLog_GetLogAppender(log);
+
+ if (!appender)
+ return FALSE;
+
+ if (!appender->active)
+ if (!WLog_OpenAppender(log))
+ return FALSE;
+
+ if (!appender->WriteDataMessage)
+ return FALSE;
+
+ EnterCriticalSection(&appender->lock);
+
+ if (appender->recursive)
+ status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
+ else
+ {
+ appender->recursive = TRUE;
+ status = appender->WriteDataMessage(log, appender, message);
+ appender->recursive = FALSE;
+ }
+
+ LeaveCriticalSection(&appender->lock);
+ return status;
+}
+
+static BOOL WLog_WriteImage(wLog* log, wLogMessage* message)
+{
+ BOOL status = 0;
+ wLogAppender* appender = NULL;
+ appender = WLog_GetLogAppender(log);
+
+ if (!appender)
+ return FALSE;
+
+ if (!appender->active)
+ if (!WLog_OpenAppender(log))
+ return FALSE;
+
+ if (!appender->WriteImageMessage)
+ return FALSE;
+
+ EnterCriticalSection(&appender->lock);
+
+ if (appender->recursive)
+ status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
+ else
+ {
+ appender->recursive = TRUE;
+ status = appender->WriteImageMessage(log, appender, message);
+ appender->recursive = FALSE;
+ }
+
+ LeaveCriticalSection(&appender->lock);
+ return status;
+}
+
+static BOOL WLog_WritePacket(wLog* log, wLogMessage* message)
+{
+ BOOL status = 0;
+ wLogAppender* appender = NULL;
+ appender = WLog_GetLogAppender(log);
+
+ if (!appender)
+ return FALSE;
+
+ if (!appender->active)
+ if (!WLog_OpenAppender(log))
+ return FALSE;
+
+ if (!appender->WritePacketMessage)
+ return FALSE;
+
+ EnterCriticalSection(&appender->lock);
+
+ if (appender->recursive)
+ status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
+ else
+ {
+ appender->recursive = TRUE;
+ status = appender->WritePacketMessage(log, appender, message);
+ appender->recursive = FALSE;
+ }
+
+ LeaveCriticalSection(&appender->lock);
+ return status;
+}
+
+BOOL WLog_PrintMessageVA(wLog* log, DWORD type, DWORD level, size_t line, const char* file,
+ const char* function, va_list args)
+{
+ BOOL status = FALSE;
+ wLogMessage message = { 0 };
+ message.Type = type;
+ message.Level = level;
+ message.LineNumber = line;
+ message.FileName = file;
+ message.FunctionName = function;
+
+ switch (type)
+ {
+ case WLOG_MESSAGE_TEXT:
+ message.FormatString = va_arg(args, const char*);
+
+ if (!strchr(message.FormatString, '%'))
+ {
+ message.TextString = message.FormatString;
+ status = WLog_Write(log, &message);
+ }
+ else
+ {
+ char formattedLogMessage[WLOG_MAX_STRING_SIZE] = { 0 };
+
+ if (vsnprintf(formattedLogMessage, WLOG_MAX_STRING_SIZE - 1, message.FormatString,
+ args) < 0)
+ return FALSE;
+
+ message.TextString = formattedLogMessage;
+ status = WLog_Write(log, &message);
+ }
+
+ break;
+
+ case WLOG_MESSAGE_DATA:
+ message.Data = va_arg(args, void*);
+ message.Length = va_arg(args, size_t);
+ status = WLog_WriteData(log, &message);
+ break;
+
+ case WLOG_MESSAGE_IMAGE:
+ message.ImageData = va_arg(args, void*);
+ message.ImageWidth = va_arg(args, size_t);
+ message.ImageHeight = va_arg(args, size_t);
+ message.ImageBpp = va_arg(args, size_t);
+ status = WLog_WriteImage(log, &message);
+ break;
+
+ case WLOG_MESSAGE_PACKET:
+ message.PacketData = va_arg(args, void*);
+ message.PacketLength = va_arg(args, size_t);
+ message.PacketFlags = va_arg(args, unsigned);
+ status = WLog_WritePacket(log, &message);
+ break;
+
+ default:
+ break;
+ }
+
+ return status;
+}
+
+BOOL WLog_PrintMessage(wLog* log, DWORD type, DWORD level, size_t line, const char* file,
+ const char* function, ...)
+{
+ BOOL status = 0;
+ va_list args;
+ va_start(args, function);
+ status = WLog_PrintMessageVA(log, type, level, line, file, function, args);
+ va_end(args);
+ return status;
+}
+
+DWORD WLog_GetLogLevel(wLog* log)
+{
+ if (!log)
+ return WLOG_OFF;
+
+ if (log->FilterLevel <= WLOG_FILTER_NOT_INITIALIZED)
+ log->FilterLevel = WLog_GetFilterLogLevel(log);
+
+ if (log->FilterLevel > WLOG_FILTER_NOT_FILTERED)
+ return (DWORD)log->FilterLevel;
+ else if (log->Level == WLOG_LEVEL_INHERIT)
+ log->Level = WLog_GetLogLevel(log->Parent);
+
+ return log->Level;
+}
+
+BOOL WLog_IsLevelActive(wLog* _log, DWORD _log_level)
+{
+ DWORD level = 0;
+
+ if (!_log)
+ return FALSE;
+
+ level = WLog_GetLogLevel(_log);
+
+ if (level == WLOG_OFF)
+ return FALSE;
+
+ return _log_level >= level;
+}
+
+BOOL WLog_SetStringLogLevel(wLog* log, LPCSTR level)
+{
+ int lvl = 0;
+
+ if (!log || !level)
+ return FALSE;
+
+ lvl = WLog_ParseLogLevel(level);
+
+ if (lvl < 0)
+ return FALSE;
+
+ return WLog_SetLogLevel(log, (DWORD)lvl);
+}
+
+static BOOL WLog_reset_log_filters(wLog* log)
+{
+ if (!log)
+ return FALSE;
+
+ log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED;
+
+ for (DWORD x = 0; x < log->ChildrenCount; x++)
+ {
+ wLog* child = log->Children[x];
+
+ if (!WLog_reset_log_filters(child))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static BOOL WLog_AddStringLogFilters_int(wLog* root, LPCSTR filter)
+{
+ LPSTR p = NULL;
+ LPCSTR filterStr = NULL;
+
+ if (!filter)
+ return FALSE;
+
+ DWORD count = 1;
+ LPCSTR cpp = filter;
+
+ while ((cpp = strchr(cpp, ',')) != NULL)
+ {
+ count++;
+ cpp++;
+ }
+
+ DWORD pos = g_FilterCount;
+ DWORD size = g_FilterCount + count;
+ wLogFilter* tmp = (wLogFilter*)realloc(g_Filters, size * sizeof(wLogFilter));
+
+ if (!tmp)
+ return FALSE;
+
+ g_Filters = tmp;
+ LPSTR cp = (LPSTR)_strdup(filter);
+
+ if (!cp)
+ return FALSE;
+
+ p = cp;
+ filterStr = cp;
+
+ do
+ {
+ p = strchr(p, ',');
+
+ if (p)
+ *p = '\0';
+
+ if (pos < size)
+ {
+ if (!WLog_ParseFilter(root, &g_Filters[pos++], filterStr))
+ {
+ free(cp);
+ return FALSE;
+ }
+ }
+ else
+ break;
+
+ if (p)
+ {
+ filterStr = p + 1;
+ p++;
+ }
+ } while (p != NULL);
+
+ g_FilterCount = size;
+ free(cp);
+ return WLog_reset_log_filters(root);
+}
+
+BOOL WLog_AddStringLogFilters(LPCSTR filter)
+{
+ /* Ensure logger is initialized */
+ wLog* root = WLog_GetRoot();
+ return WLog_AddStringLogFilters_int(root, filter);
+}
+
+static BOOL WLog_UpdateInheritLevel(wLog* log, DWORD logLevel)
+{
+ if (!log)
+ return FALSE;
+
+ if (log->inherit)
+ {
+ log->Level = logLevel;
+
+ for (DWORD x = 0; x < log->ChildrenCount; x++)
+ {
+ wLog* child = log->Children[x];
+
+ if (!WLog_UpdateInheritLevel(child, logLevel))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL WLog_SetLogLevel(wLog* log, DWORD logLevel)
+{
+ if (!log)
+ return FALSE;
+
+ if ((logLevel > WLOG_OFF) && (logLevel != WLOG_LEVEL_INHERIT))
+ logLevel = WLOG_OFF;
+
+ log->Level = logLevel;
+ log->inherit = (logLevel == WLOG_LEVEL_INHERIT) ? TRUE : FALSE;
+
+ for (DWORD x = 0; x < log->ChildrenCount; x++)
+ {
+ wLog* child = log->Children[x];
+
+ if (!WLog_UpdateInheritLevel(child, logLevel))
+ return FALSE;
+ }
+
+ return WLog_reset_log_filters(log);
+}
+
+int WLog_ParseLogLevel(LPCSTR level)
+{
+ int iLevel = -1;
+
+ if (!level)
+ return -1;
+
+ if (_stricmp(level, "TRACE") == 0)
+ iLevel = WLOG_TRACE;
+ else if (_stricmp(level, "DEBUG") == 0)
+ iLevel = WLOG_DEBUG;
+ else if (_stricmp(level, "INFO") == 0)
+ iLevel = WLOG_INFO;
+ else if (_stricmp(level, "WARN") == 0)
+ iLevel = WLOG_WARN;
+ else if (_stricmp(level, "ERROR") == 0)
+ iLevel = WLOG_ERROR;
+ else if (_stricmp(level, "FATAL") == 0)
+ iLevel = WLOG_FATAL;
+ else if (_stricmp(level, "OFF") == 0)
+ iLevel = WLOG_OFF;
+
+ return iLevel;
+}
+
+BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name)
+{
+ const char* pc = NULL;
+ char* p = NULL;
+ char* q = NULL;
+ size_t count = 0;
+ LPSTR names = NULL;
+ int iLevel = 0;
+ count = 1;
+
+ WINPR_UNUSED(root);
+
+ if (!name)
+ return FALSE;
+
+ pc = name;
+
+ if (pc)
+ {
+ while ((pc = strchr(pc, '.')) != NULL)
+ {
+ count++;
+ pc++;
+ }
+ }
+
+ names = _strdup(name);
+
+ if (!names)
+ return FALSE;
+
+ filter->NameCount = count;
+ filter->Names = (LPSTR*)calloc((count + 1UL), sizeof(LPSTR));
+
+ if (!filter->Names)
+ {
+ free(names);
+ filter->NameCount = 0;
+ return FALSE;
+ }
+
+ filter->Names[count] = NULL;
+ count = 0;
+ p = (char*)names;
+ filter->Names[count++] = p;
+ q = strrchr(p, ':');
+
+ if (!q)
+ {
+ free(names);
+ free(filter->Names);
+ filter->Names = NULL;
+ filter->NameCount = 0;
+ return FALSE;
+ }
+
+ *q = '\0';
+ q++;
+ iLevel = WLog_ParseLogLevel(q);
+
+ if (iLevel < 0)
+ {
+ free(names);
+ free(filter->Names);
+ filter->Names = NULL;
+ filter->NameCount = 0;
+ return FALSE;
+ }
+
+ filter->Level = (DWORD)iLevel;
+
+ while ((p = strchr(p, '.')) != NULL)
+ {
+ if (count < filter->NameCount)
+ filter->Names[count++] = p + 1;
+
+ *p = '\0';
+ p++;
+ }
+
+ return TRUE;
+}
+
+BOOL WLog_ParseFilters(wLog* root)
+{
+ LPCSTR filter = "WLOG_FILTER";
+ BOOL res = FALSE;
+ char* env = NULL;
+ DWORD nSize = 0;
+ free(g_Filters);
+ g_Filters = NULL;
+ g_FilterCount = 0;
+ nSize = GetEnvironmentVariableA(filter, NULL, 0);
+
+ if (nSize < 1)
+ return TRUE;
+
+ env = (LPSTR)malloc(nSize);
+
+ if (!env)
+ return FALSE;
+
+ if (GetEnvironmentVariableA(filter, env, nSize) == nSize - 1)
+ res = WLog_AddStringLogFilters_int(root, env);
+
+ free(env);
+ return res;
+}
+
+LONG WLog_GetFilterLogLevel(wLog* log)
+{
+ BOOL match = FALSE;
+
+ if (log->FilterLevel >= 0)
+ return log->FilterLevel;
+
+ log->FilterLevel = WLOG_FILTER_NOT_FILTERED;
+ for (DWORD i = 0; i < g_FilterCount; i++)
+ {
+ const wLogFilter* filter = &g_Filters[i];
+ for (DWORD j = 0; j < filter->NameCount; j++)
+ {
+ if (j >= log->NameCount)
+ break;
+
+ if (_stricmp(filter->Names[j], "*") == 0)
+ {
+ match = TRUE;
+ log->FilterLevel = filter->Level;
+ break;
+ }
+
+ if (_stricmp(filter->Names[j], log->Names[j]) != 0)
+ break;
+
+ if (j == (log->NameCount - 1))
+ {
+ match = log->NameCount == filter->NameCount;
+ if (match)
+ log->FilterLevel = filter->Level;
+ break;
+ }
+ }
+
+ if (match)
+ break;
+ }
+
+ return log->FilterLevel;
+}
+
+static BOOL WLog_ParseName(wLog* log, LPCSTR name)
+{
+ const char* cp = name;
+ char* p = NULL;
+ size_t count = 1;
+ LPSTR names = NULL;
+
+ while ((cp = strchr(cp, '.')) != NULL)
+ {
+ count++;
+ cp++;
+ }
+
+ names = _strdup(name);
+
+ if (!names)
+ return FALSE;
+
+ log->NameCount = count;
+ log->Names = (LPSTR*)calloc((count + 1UL), sizeof(LPSTR));
+
+ if (!log->Names)
+ {
+ free(names);
+ return FALSE;
+ }
+
+ log->Names[count] = NULL;
+ count = 0;
+ p = (char*)names;
+ log->Names[count++] = p;
+
+ while ((p = strchr(p, '.')) != NULL)
+ {
+ if (count < log->NameCount)
+ log->Names[count++] = p + 1;
+
+ *p = '\0';
+ p++;
+ }
+
+ return TRUE;
+}
+
+wLog* WLog_New(LPCSTR name, wLog* rootLogger)
+{
+ wLog* log = NULL;
+ char* env = NULL;
+ DWORD nSize = 0;
+ int iLevel = 0;
+ log = (wLog*)calloc(1, sizeof(wLog));
+
+ if (!log)
+ return NULL;
+
+ log->Name = _strdup(name);
+
+ if (!log->Name)
+ goto out_fail;
+
+ if (!WLog_ParseName(log, name))
+ goto out_fail;
+
+ log->Parent = rootLogger;
+ log->ChildrenCount = 0;
+ log->ChildrenSize = 16;
+ log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED;
+
+ if (!(log->Children = (wLog**)calloc(log->ChildrenSize, sizeof(wLog*))))
+ goto out_fail;
+
+ log->Appender = NULL;
+
+ if (rootLogger)
+ {
+ log->Level = WLOG_LEVEL_INHERIT;
+ log->inherit = TRUE;
+ }
+ else
+ {
+ LPCSTR level = "WLOG_LEVEL";
+ log->Level = WLOG_INFO;
+ nSize = GetEnvironmentVariableA(level, NULL, 0);
+
+ if (nSize)
+ {
+ env = (LPSTR)malloc(nSize);
+
+ if (!env)
+ goto out_fail;
+
+ if (GetEnvironmentVariableA(level, env, nSize) != nSize - 1)
+ {
+ fprintf(stderr, "%s environment variable changed in my back !\n", level);
+ free(env);
+ goto out_fail;
+ }
+
+ iLevel = WLog_ParseLogLevel(env);
+ free(env);
+
+ if (iLevel >= 0)
+ {
+ if (!WLog_SetLogLevel(log, (DWORD)iLevel))
+ goto out_fail;
+ }
+ }
+ }
+
+ iLevel = WLog_GetFilterLogLevel(log);
+
+ if (iLevel >= 0)
+ {
+ if (!WLog_SetLogLevel(log, (DWORD)iLevel))
+ goto out_fail;
+ }
+
+ InitializeCriticalSectionAndSpinCount(&log->lock, 4000);
+
+ return log;
+out_fail:
+ free(log->Children);
+ free(log->Name);
+ free(log);
+ return NULL;
+}
+
+void WLog_Free(wLog* log)
+{
+ if (log)
+ {
+ if (log->Appender)
+ {
+ WLog_Appender_Free(log, log->Appender);
+ log->Appender = NULL;
+ }
+
+ free(log->Name);
+ free(log->Names[0]);
+ free(log->Names);
+ free(log->Children);
+ DeleteCriticalSection(&log->lock);
+ free(log);
+ }
+}
+
+wLog* WLog_GetRoot(void)
+{
+ if (!InitOnceExecuteOnce(&_WLogInitialized, WLog_InitializeRoot, NULL, NULL))
+ return NULL;
+
+ return g_RootLog;
+}
+
+static BOOL WLog_AddChild(wLog* parent, wLog* child)
+{
+ BOOL status = FALSE;
+
+ WLog_Lock(parent);
+
+ if (parent->ChildrenCount >= parent->ChildrenSize)
+ {
+ wLog** tmp = NULL;
+ parent->ChildrenSize *= 2;
+
+ if (!parent->ChildrenSize)
+ {
+ if (parent->Children)
+ free(parent->Children);
+
+ parent->Children = NULL;
+ }
+ else
+ {
+ tmp = (wLog**)realloc(parent->Children, sizeof(wLog*) * parent->ChildrenSize);
+
+ if (!tmp)
+ {
+ if (parent->Children)
+ free(parent->Children);
+
+ parent->Children = NULL;
+ goto exit;
+ }
+
+ parent->Children = tmp;
+ }
+ }
+
+ if (!parent->Children)
+ goto exit;
+
+ parent->Children[parent->ChildrenCount++] = child;
+ child->Parent = parent;
+
+ WLog_Unlock(parent);
+
+ status = TRUE;
+exit:
+ return status;
+}
+
+static wLog* WLog_FindChild(wLog* root, LPCSTR name)
+{
+ wLog* child = NULL;
+ BOOL found = FALSE;
+
+ if (!root)
+ return NULL;
+
+ WLog_Lock(root);
+
+ for (DWORD index = 0; index < root->ChildrenCount; index++)
+ {
+ child = root->Children[index];
+
+ if (strcmp(child->Name, name) == 0)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ WLog_Unlock(root);
+
+ return (found) ? child : NULL;
+}
+
+static wLog* WLog_Get_int(wLog* root, LPCSTR name)
+{
+ wLog* log = NULL;
+
+ if (!(log = WLog_FindChild(root, name)))
+ {
+ if (!root)
+ return NULL;
+
+ if (!(log = WLog_New(name, root)))
+ return NULL;
+
+ if (!WLog_AddChild(root, log))
+ {
+ WLog_Free(log);
+ return NULL;
+ }
+ }
+
+ return log;
+}
+
+wLog* WLog_Get(LPCSTR name)
+{
+ wLog* root = WLog_GetRoot();
+ return WLog_Get_int(root, name);
+}
+
+#if defined(WITH_WINPR_DEPRECATED)
+BOOL WLog_Init(void)
+{
+ return WLog_GetRoot() != NULL;
+}
+
+BOOL WLog_Uninit(void)
+{
+ wLog* root = g_RootLog;
+
+ if (!root)
+ return FALSE;
+
+ WLog_Lock(root);
+
+ for (DWORD index = 0; index < root->ChildrenCount; index++)
+ {
+ wLog* child = root->Children[index];
+ WLog_Free(child);
+ }
+
+ WLog_Unlock(root);
+
+ WLog_Free(root);
+ g_RootLog = NULL;
+
+ return TRUE;
+}
+#endif
+
+BOOL WLog_SetContext(wLog* log, const char* (*fkt)(void*), void* context)
+{
+ WINPR_ASSERT(log);
+
+ log->custom = fkt;
+ log->context = context;
+ return TRUE;
+}