diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
commit | a9bcc81f821d7c66f623779fa5147e728eb3c388 (patch) | |
tree | 98676963bcdd537ae5908a067a8eb110b93486a6 /winpr/libwinpr/utils/wlog | |
parent | Initial commit. (diff) | |
download | freerdp3-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')
28 files changed, 4367 insertions, 0 deletions
diff --git a/winpr/libwinpr/utils/wlog/Appender.c b/winpr/libwinpr/utils/wlog/Appender.c new file mode 100644 index 0000000..a1cbbd6 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/Appender.c @@ -0,0 +1,176 @@ +/** + * 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 "Appender.h" + +void WLog_Appender_Free(wLog* log, wLogAppender* appender) +{ + if (!appender) + return; + + if (appender->Layout) + { + WLog_Layout_Free(log, appender->Layout); + appender->Layout = NULL; + } + + DeleteCriticalSection(&appender->lock); + appender->Free(appender); +} + +wLogAppender* WLog_GetLogAppender(wLog* log) +{ + if (!log) + return NULL; + + if (!log->Appender) + return WLog_GetLogAppender(log->Parent); + + return log->Appender; +} + +BOOL WLog_OpenAppender(wLog* log) +{ + int status = 0; + wLogAppender* appender = NULL; + + appender = WLog_GetLogAppender(log); + + if (!appender) + return FALSE; + + if (!appender->Open) + return TRUE; + + if (!appender->active) + { + status = appender->Open(log, appender); + appender->active = TRUE; + } + + return status; +} + +BOOL WLog_CloseAppender(wLog* log) +{ + int status = 0; + wLogAppender* appender = NULL; + + appender = WLog_GetLogAppender(log); + + if (!appender) + return FALSE; + + if (!appender->Close) + return TRUE; + + if (appender->active) + { + status = appender->Close(log, appender); + appender->active = FALSE; + } + + return status; +} + +static wLogAppender* WLog_Appender_New(wLog* log, DWORD logAppenderType) +{ + wLogAppender* appender = NULL; + + if (!log) + return NULL; + + switch (logAppenderType) + { + case WLOG_APPENDER_CONSOLE: + appender = WLog_ConsoleAppender_New(log); + break; + case WLOG_APPENDER_FILE: + appender = WLog_FileAppender_New(log); + break; + case WLOG_APPENDER_BINARY: + appender = WLog_BinaryAppender_New(log); + break; + case WLOG_APPENDER_CALLBACK: + appender = WLog_CallbackAppender_New(log); + break; +#ifdef WINPR_HAVE_SYSLOG_H + case WLOG_APPENDER_SYSLOG: + appender = WLog_SyslogAppender_New(log); + break; +#endif +#ifdef WINPR_HAVE_JOURNALD_H + case WLOG_APPENDER_JOURNALD: + appender = WLog_JournaldAppender_New(log); + break; +#endif + case WLOG_APPENDER_UDP: + appender = (wLogAppender*)WLog_UdpAppender_New(log); + break; + default: + fprintf(stderr, "%s: unknown handler type %" PRIu32 "\n", __func__, logAppenderType); + appender = NULL; + break; + } + + if (!appender) + appender = (wLogAppender*)WLog_ConsoleAppender_New(log); + + if (!appender) + return NULL; + + if (!(appender->Layout = WLog_Layout_New(log))) + { + WLog_Appender_Free(log, appender); + return NULL; + } + + InitializeCriticalSectionAndSpinCount(&appender->lock, 4000); + + return appender; +} + +BOOL WLog_SetLogAppenderType(wLog* log, DWORD logAppenderType) +{ + if (!log) + return FALSE; + + if (log->Appender) + { + WLog_Appender_Free(log, log->Appender); + log->Appender = NULL; + } + + log->Appender = WLog_Appender_New(log, logAppenderType); + return log->Appender != NULL; +} + +BOOL WLog_ConfigureAppender(wLogAppender* appender, const char* setting, void* value) +{ + /* Just check the settings string is not empty */ + if (!appender || !setting || (strnlen(setting, 2) == 0)) + return FALSE; + + if (appender->Set) + return appender->Set(appender, setting, value); + else + return FALSE; +} diff --git a/winpr/libwinpr/utils/wlog/Appender.h b/winpr/libwinpr/utils/wlog/Appender.h new file mode 100644 index 0000000..00f8119 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/Appender.h @@ -0,0 +1,39 @@ +/** + * 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. + */ + +#ifndef WINPR_WLOG_APPENDER_PRIVATE_H +#define WINPR_WLOG_APPENDER_PRIVATE_H + +#include "wlog.h" + +void WLog_Appender_Free(wLog* log, wLogAppender* appender); + +#include "FileAppender.h" +#include "ConsoleAppender.h" +#include "BinaryAppender.h" +#include "CallbackAppender.h" +#ifdef WINPR_HAVE_JOURNALD_H +#include "JournaldAppender.h" +#endif +#ifdef WINPR_HAVE_SYSLOG_H +#include "SyslogAppender.h" +#endif +#include "UdpAppender.h" + +#endif /* WINPR_WLOG_APPENDER_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/BinaryAppender.c b/winpr/libwinpr/utils/wlog/BinaryAppender.c new file mode 100644 index 0000000..e9a440a --- /dev/null +++ b/winpr/libwinpr/utils/wlog/BinaryAppender.c @@ -0,0 +1,238 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.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 "BinaryAppender.h" +#include <winpr/crt.h> +#include <winpr/assert.h> +#include <winpr/file.h> +#include <winpr/path.h> +#include <winpr/stream.h> + +typedef struct +{ + WLOG_APPENDER_COMMON(); + + char* FileName; + char* FilePath; + char* FullFileName; + FILE* FileDescriptor; +} wLogBinaryAppender; + +static BOOL WLog_BinaryAppender_Open(wLog* log, wLogAppender* appender) +{ + wLogBinaryAppender* binaryAppender = NULL; + if (!log || !appender) + return FALSE; + + binaryAppender = (wLogBinaryAppender*)appender; + if (!binaryAppender->FileName) + { + binaryAppender->FileName = (char*)malloc(MAX_PATH); + if (!binaryAppender->FileName) + return FALSE; + sprintf_s(binaryAppender->FileName, MAX_PATH, "%" PRIu32 ".wlog", GetCurrentProcessId()); + } + + if (!binaryAppender->FilePath) + { + binaryAppender->FilePath = GetKnownSubPath(KNOWN_PATH_TEMP, "wlog"); + if (!binaryAppender->FilePath) + return FALSE; + } + + if (!binaryAppender->FullFileName) + { + binaryAppender->FullFileName = + GetCombinedPath(binaryAppender->FilePath, binaryAppender->FileName); + if (!binaryAppender->FullFileName) + return FALSE; + } + + if (!winpr_PathFileExists(binaryAppender->FilePath)) + { + if (!winpr_PathMakePath(binaryAppender->FilePath, 0)) + return FALSE; + UnixChangeFileMode(binaryAppender->FilePath, 0xFFFF); + } + + binaryAppender->FileDescriptor = winpr_fopen(binaryAppender->FullFileName, "a+"); + + if (!binaryAppender->FileDescriptor) + return FALSE; + + return TRUE; +} + +static BOOL WLog_BinaryAppender_Close(wLog* log, wLogAppender* appender) +{ + wLogBinaryAppender* binaryAppender = NULL; + + if (!appender) + return FALSE; + + binaryAppender = (wLogBinaryAppender*)appender; + if (!binaryAppender->FileDescriptor) + return TRUE; + + if (binaryAppender->FileDescriptor) + fclose(binaryAppender->FileDescriptor); + + binaryAppender->FileDescriptor = NULL; + + return TRUE; +} + +static BOOL WLog_BinaryAppender_WriteMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + FILE* fp = NULL; + wStream* s = NULL; + size_t MessageLength = 0; + size_t FileNameLength = 0; + size_t FunctionNameLength = 0; + size_t TextStringLength = 0; + BOOL ret = TRUE; + wLogBinaryAppender* binaryAppender = NULL; + + if (!log || !appender || !message) + return FALSE; + + binaryAppender = (wLogBinaryAppender*)appender; + + fp = binaryAppender->FileDescriptor; + + if (!fp) + return FALSE; + + FileNameLength = strnlen(message->FileName, INT_MAX); + FunctionNameLength = strnlen(message->FunctionName, INT_MAX); + TextStringLength = strnlen(message->TextString, INT_MAX); + + MessageLength = + 16 + (4 + FileNameLength + 1) + (4 + FunctionNameLength + 1) + (4 + TextStringLength + 1); + + if ((MessageLength > UINT32_MAX) || (FileNameLength > UINT32_MAX) || + (FunctionNameLength > UINT32_MAX) || (TextStringLength > UINT32_MAX)) + return FALSE; + + s = Stream_New(NULL, MessageLength); + if (!s) + return FALSE; + + Stream_Write_UINT32(s, (UINT32)MessageLength); + + Stream_Write_UINT32(s, message->Type); + Stream_Write_UINT32(s, message->Level); + + WINPR_ASSERT(message->LineNumber <= UINT32_MAX); + Stream_Write_UINT32(s, (UINT32)message->LineNumber); + + Stream_Write_UINT32(s, (UINT32)FileNameLength); + Stream_Write(s, message->FileName, FileNameLength + 1); + + Stream_Write_UINT32(s, (UINT32)FunctionNameLength); + Stream_Write(s, message->FunctionName, FunctionNameLength + 1); + + Stream_Write_UINT32(s, (UINT32)TextStringLength); + Stream_Write(s, message->TextString, TextStringLength + 1); + + Stream_SealLength(s); + + if (fwrite(Stream_Buffer(s), MessageLength, 1, fp) != 1) + ret = FALSE; + + Stream_Free(s, TRUE); + + return ret; +} + +static BOOL WLog_BinaryAppender_WriteDataMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + return TRUE; +} + +static BOOL WLog_BinaryAppender_WriteImageMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + return TRUE; +} + +static BOOL WLog_BinaryAppender_Set(wLogAppender* appender, const char* setting, void* value) +{ + wLogBinaryAppender* binaryAppender = (wLogBinaryAppender*)appender; + + /* Just check if the value string is longer than 0 */ + if (!value || (strnlen(value, 2) == 0)) + return FALSE; + + if (!strcmp("outputfilename", setting)) + { + binaryAppender->FileName = _strdup((const char*)value); + if (!binaryAppender->FileName) + return FALSE; + } + else if (!strcmp("outputfilepath", setting)) + { + binaryAppender->FilePath = _strdup((const char*)value); + if (!binaryAppender->FilePath) + return FALSE; + } + else + return FALSE; + + return TRUE; +} + +static void WLog_BinaryAppender_Free(wLogAppender* appender) +{ + wLogBinaryAppender* binaryAppender = NULL; + if (appender) + { + binaryAppender = (wLogBinaryAppender*)appender; + free(binaryAppender->FileName); + free(binaryAppender->FilePath); + free(binaryAppender->FullFileName); + free(binaryAppender); + } +} + +wLogAppender* WLog_BinaryAppender_New(wLog* log) +{ + wLogBinaryAppender* BinaryAppender = NULL; + + BinaryAppender = (wLogBinaryAppender*)calloc(1, sizeof(wLogBinaryAppender)); + if (!BinaryAppender) + return NULL; + + BinaryAppender->Type = WLOG_APPENDER_BINARY; + BinaryAppender->Open = WLog_BinaryAppender_Open; + BinaryAppender->Close = WLog_BinaryAppender_Close; + BinaryAppender->WriteMessage = WLog_BinaryAppender_WriteMessage; + BinaryAppender->WriteDataMessage = WLog_BinaryAppender_WriteDataMessage; + BinaryAppender->WriteImageMessage = WLog_BinaryAppender_WriteImageMessage; + BinaryAppender->Free = WLog_BinaryAppender_Free; + BinaryAppender->Set = WLog_BinaryAppender_Set; + + return (wLogAppender*)BinaryAppender; +} diff --git a/winpr/libwinpr/utils/wlog/BinaryAppender.h b/winpr/libwinpr/utils/wlog/BinaryAppender.h new file mode 100644 index 0000000..fb65d9f --- /dev/null +++ b/winpr/libwinpr/utils/wlog/BinaryAppender.h @@ -0,0 +1,28 @@ +/** + * 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. + */ + +#ifndef WINPR_WLOG_BINARY_APPENDER_PRIVATE_H +#define WINPR_WLOG_BINARY_APPENDER_PRIVATE_H + +#include "wlog.h" + +WINPR_ATTR_MALLOC(WLog_Appender_Free, 2) +wLogAppender* WLog_BinaryAppender_New(wLog* log); + +#endif /* WINPR_WLOG_BINARY_APPENDER_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/CallbackAppender.c b/winpr/libwinpr/utils/wlog/CallbackAppender.c new file mode 100644 index 0000000..f324e7c --- /dev/null +++ b/winpr/libwinpr/utils/wlog/CallbackAppender.c @@ -0,0 +1,168 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2014 Armin Novak <armin.novak@thincast.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 "CallbackAppender.h" + +typedef struct +{ + WLOG_APPENDER_COMMON(); + + wLogCallbacks* callbacks; +} wLogCallbackAppender; + +static BOOL WLog_CallbackAppender_Open(wLog* log, wLogAppender* appender) +{ + return TRUE; +} + +static BOOL WLog_CallbackAppender_Close(wLog* log, wLogAppender* appender) +{ + return TRUE; +} + +static BOOL WLog_CallbackAppender_WriteMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + wLogCallbackAppender* callbackAppender = NULL; + + if (!appender) + return FALSE; + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + + callbackAppender = (wLogCallbackAppender*)appender; + + if (callbackAppender->callbacks && callbackAppender->callbacks->message) + return callbackAppender->callbacks->message(message); + else + return FALSE; +} + +static BOOL WLog_CallbackAppender_WriteDataMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + wLogCallbackAppender* callbackAppender = NULL; + + if (!appender) + return FALSE; + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + + callbackAppender = (wLogCallbackAppender*)appender; + if (callbackAppender->callbacks && callbackAppender->callbacks->data) + return callbackAppender->callbacks->data(message); + else + return FALSE; +} + +static BOOL WLog_CallbackAppender_WriteImageMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + wLogCallbackAppender* callbackAppender = NULL; + + if (!appender) + return FALSE; + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + + callbackAppender = (wLogCallbackAppender*)appender; + if (callbackAppender->callbacks && callbackAppender->callbacks->image) + return callbackAppender->callbacks->image(message); + else + return FALSE; +} + +static BOOL WLog_CallbackAppender_WritePacketMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + wLogCallbackAppender* callbackAppender = NULL; + + if (!appender) + return FALSE; + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + + callbackAppender = (wLogCallbackAppender*)appender; + if (callbackAppender->callbacks && callbackAppender->callbacks->package) + return callbackAppender->callbacks->package(message); + else + return FALSE; +} + +static BOOL WLog_CallbackAppender_Set(wLogAppender* appender, const char* setting, void* value) +{ + wLogCallbackAppender* callbackAppender = (wLogCallbackAppender*)appender; + + if (!value || strcmp(setting, "callbacks")) + return FALSE; + + if (!(callbackAppender->callbacks = calloc(1, sizeof(wLogCallbacks)))) + { + return FALSE; + } + + callbackAppender->callbacks = memcpy(callbackAppender->callbacks, value, sizeof(wLogCallbacks)); + return TRUE; +} + +static void WLog_CallbackAppender_Free(wLogAppender* appender) +{ + wLogCallbackAppender* callbackAppender = NULL; + if (!appender) + { + return; + } + + callbackAppender = (wLogCallbackAppender*)appender; + + free(callbackAppender->callbacks); + free(appender); +} + +wLogAppender* WLog_CallbackAppender_New(wLog* log) +{ + wLogCallbackAppender* CallbackAppender = NULL; + + CallbackAppender = (wLogCallbackAppender*)calloc(1, sizeof(wLogCallbackAppender)); + if (!CallbackAppender) + return NULL; + + CallbackAppender->Type = WLOG_APPENDER_CALLBACK; + + CallbackAppender->Open = WLog_CallbackAppender_Open; + CallbackAppender->Close = WLog_CallbackAppender_Close; + CallbackAppender->WriteMessage = WLog_CallbackAppender_WriteMessage; + CallbackAppender->WriteDataMessage = WLog_CallbackAppender_WriteDataMessage; + CallbackAppender->WriteImageMessage = WLog_CallbackAppender_WriteImageMessage; + CallbackAppender->WritePacketMessage = WLog_CallbackAppender_WritePacketMessage; + CallbackAppender->Free = WLog_CallbackAppender_Free; + CallbackAppender->Set = WLog_CallbackAppender_Set; + + return (wLogAppender*)CallbackAppender; +} diff --git a/winpr/libwinpr/utils/wlog/CallbackAppender.h b/winpr/libwinpr/utils/wlog/CallbackAppender.h new file mode 100644 index 0000000..cd10f7d --- /dev/null +++ b/winpr/libwinpr/utils/wlog/CallbackAppender.h @@ -0,0 +1,28 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2014 Armin Novak <armin.novak@thincast.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. + */ + +#ifndef WINPR_WLOG_CALLBACK_APPENDER_PRIVATE_H +#define WINPR_WLOG_CALLBACK_APPENDER_PRIVATE_H + +#include "wlog.h" + +WINPR_ATTR_MALLOC(WLog_Appender_Free, 2) +wLogAppender* WLog_CallbackAppender_New(wLog* log); + +#endif /* WINPR_WLOG_CALLBACK_APPENDER_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/ConsoleAppender.c b/winpr/libwinpr/utils/wlog/ConsoleAppender.c new file mode 100644 index 0000000..0a50ef6 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/ConsoleAppender.c @@ -0,0 +1,276 @@ +/** + * 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 "ConsoleAppender.h" +#include "Message.h" + +#ifdef ANDROID +#include <android/log.h> +#endif + +#define WLOG_CONSOLE_DEFAULT 0 +#define WLOG_CONSOLE_STDOUT 1 +#define WLOG_CONSOLE_STDERR 2 +#define WLOG_CONSOLE_DEBUG 4 + +typedef struct +{ + WLOG_APPENDER_COMMON(); + + int outputStream; +} wLogConsoleAppender; + +static BOOL WLog_ConsoleAppender_Open(wLog* log, wLogAppender* appender) +{ + return TRUE; +} + +static BOOL WLog_ConsoleAppender_Close(wLog* log, wLogAppender* appender) +{ + return TRUE; +} + +static BOOL WLog_ConsoleAppender_WriteMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + FILE* fp = NULL; + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + wLogConsoleAppender* consoleAppender = NULL; + if (!appender) + return FALSE; + + consoleAppender = (wLogConsoleAppender*)appender; + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + +#ifdef _WIN32 + if (consoleAppender->outputStream == WLOG_CONSOLE_DEBUG) + { + OutputDebugStringA(message->PrefixString); + OutputDebugStringA(message->TextString); + OutputDebugStringA("\n"); + + return TRUE; + } +#endif +#ifdef ANDROID + (void)fp; + android_LogPriority level; + switch (message->Level) + { + case WLOG_TRACE: + level = ANDROID_LOG_VERBOSE; + break; + case WLOG_DEBUG: + level = ANDROID_LOG_DEBUG; + break; + case WLOG_INFO: + level = ANDROID_LOG_INFO; + break; + case WLOG_WARN: + level = ANDROID_LOG_WARN; + break; + case WLOG_ERROR: + level = ANDROID_LOG_ERROR; + break; + case WLOG_FATAL: + level = ANDROID_LOG_FATAL; + break; + case WLOG_OFF: + level = ANDROID_LOG_SILENT; + break; + default: + level = ANDROID_LOG_FATAL; + break; + } + + if (level != ANDROID_LOG_SILENT) + __android_log_print(level, log->Name, "%s%s", message->PrefixString, message->TextString); + +#else + switch (consoleAppender->outputStream) + { + case WLOG_CONSOLE_STDOUT: + fp = stdout; + break; + case WLOG_CONSOLE_STDERR: + fp = stderr; + break; + default: + switch (message->Level) + { + case WLOG_TRACE: + case WLOG_DEBUG: + case WLOG_INFO: + fp = stdout; + break; + default: + fp = stderr; + break; + } + break; + } + + if (message->Level != WLOG_OFF) + fprintf(fp, "%s%s\n", message->PrefixString, message->TextString); +#endif + return TRUE; +} + +static int g_DataId = 0; + +static BOOL WLog_ConsoleAppender_WriteDataMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ +#if defined(ANDROID) + return FALSE; +#else + int DataId = 0; + char* FullFileName = NULL; + + DataId = g_DataId++; + FullFileName = WLog_Message_GetOutputFileName(DataId, "dat"); + + WLog_DataMessage_Write(FullFileName, message->Data, message->Length); + + free(FullFileName); + + return TRUE; +#endif +} + +static int g_ImageId = 0; + +static BOOL WLog_ConsoleAppender_WriteImageMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ +#if defined(ANDROID) + return FALSE; +#else + int ImageId = 0; + char* FullFileName = NULL; + + ImageId = g_ImageId++; + FullFileName = WLog_Message_GetOutputFileName(ImageId, "bmp"); + + WLog_ImageMessage_Write(FullFileName, message->ImageData, message->ImageWidth, + message->ImageHeight, message->ImageBpp); + + free(FullFileName); + + return TRUE; +#endif +} + +static int g_PacketId = 0; + +static BOOL WLog_ConsoleAppender_WritePacketMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ +#if defined(ANDROID) + return FALSE; +#else + char* FullFileName = NULL; + + g_PacketId++; + + if (!appender->PacketMessageContext) + { + FullFileName = WLog_Message_GetOutputFileName(-1, "pcap"); + appender->PacketMessageContext = (void*)Pcap_Open(FullFileName, TRUE); + free(FullFileName); + } + + if (appender->PacketMessageContext) + return WLog_PacketMessage_Write((wPcap*)appender->PacketMessageContext, message->PacketData, + message->PacketLength, message->PacketFlags); + + return TRUE; +#endif +} +static BOOL WLog_ConsoleAppender_Set(wLogAppender* appender, const char* setting, void* value) +{ + wLogConsoleAppender* consoleAppender = (wLogConsoleAppender*)appender; + + /* Just check the value string is not empty */ + if (!value || (strnlen(value, 2) == 0)) + return FALSE; + + if (strcmp("outputstream", setting)) + return FALSE; + + if (!strcmp("stdout", value)) + consoleAppender->outputStream = WLOG_CONSOLE_STDOUT; + else if (!strcmp("stderr", value)) + consoleAppender->outputStream = WLOG_CONSOLE_STDERR; + else if (!strcmp("default", value)) + consoleAppender->outputStream = WLOG_CONSOLE_DEFAULT; + else if (!strcmp("debug", value)) + consoleAppender->outputStream = WLOG_CONSOLE_DEBUG; + else + return FALSE; + + return TRUE; +} + +static void WLog_ConsoleAppender_Free(wLogAppender* appender) +{ + if (appender) + { + if (appender->PacketMessageContext) + { + Pcap_Close((wPcap*)appender->PacketMessageContext); + } + + free(appender); + } +} + +wLogAppender* WLog_ConsoleAppender_New(wLog* log) +{ + wLogConsoleAppender* ConsoleAppender = NULL; + + ConsoleAppender = (wLogConsoleAppender*)calloc(1, sizeof(wLogConsoleAppender)); + + if (!ConsoleAppender) + return NULL; + + ConsoleAppender->Type = WLOG_APPENDER_CONSOLE; + + ConsoleAppender->Open = WLog_ConsoleAppender_Open; + ConsoleAppender->Close = WLog_ConsoleAppender_Close; + ConsoleAppender->WriteMessage = WLog_ConsoleAppender_WriteMessage; + ConsoleAppender->WriteDataMessage = WLog_ConsoleAppender_WriteDataMessage; + ConsoleAppender->WriteImageMessage = WLog_ConsoleAppender_WriteImageMessage; + ConsoleAppender->WritePacketMessage = WLog_ConsoleAppender_WritePacketMessage; + ConsoleAppender->Set = WLog_ConsoleAppender_Set; + ConsoleAppender->Free = WLog_ConsoleAppender_Free; + + ConsoleAppender->outputStream = WLOG_CONSOLE_DEFAULT; + +#ifdef _WIN32 + if (IsDebuggerPresent()) + ConsoleAppender->outputStream = WLOG_CONSOLE_DEBUG; +#endif + + return (wLogAppender*)ConsoleAppender; +} diff --git a/winpr/libwinpr/utils/wlog/ConsoleAppender.h b/winpr/libwinpr/utils/wlog/ConsoleAppender.h new file mode 100644 index 0000000..f6a1405 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/ConsoleAppender.h @@ -0,0 +1,28 @@ +/** + * 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. + */ + +#ifndef WINPR_WLOG_CONSOLE_APPENDER_PRIVATE_H +#define WINPR_WLOG_CONSOLE_APPENDER_PRIVATE_H + +#include "wlog.h" + +WINPR_ATTR_MALLOC(WLog_Appender_Free, 2) +wLogAppender* WLog_ConsoleAppender_New(wLog* log); + +#endif /* WINPR_WLOG_CONSOLE_APPENDER_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/DataMessage.c b/winpr/libwinpr/utils/wlog/DataMessage.c new file mode 100644 index 0000000..512fddd --- /dev/null +++ b/winpr/libwinpr/utils/wlog/DataMessage.c @@ -0,0 +1,48 @@ +/** + * 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 "wlog.h" + +#include "DataMessage.h" + +#include <winpr/file.h> + +#include "../../log.h" +#define TAG WINPR_TAG("utils.wlog") + +BOOL WLog_DataMessage_Write(const char* filename, const void* data, size_t length) +{ + FILE* fp = NULL; + BOOL ret = TRUE; + + fp = winpr_fopen(filename, "w+b"); + + if (!fp) + { + // WLog_ERR(TAG, "failed to open file %s", filename); + return FALSE; + } + + if (fwrite(data, length, 1, fp) != 1) + ret = FALSE; + fclose(fp); + return ret; +} diff --git a/winpr/libwinpr/utils/wlog/DataMessage.h b/winpr/libwinpr/utils/wlog/DataMessage.h new file mode 100644 index 0000000..db2c09e --- /dev/null +++ b/winpr/libwinpr/utils/wlog/DataMessage.h @@ -0,0 +1,25 @@ +/** + * 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. + */ + +#ifndef WINPR_WLOG_DATA_MESSAGE_PRIVATE_H +#define WINPR_WLOG_DATA_MESSAGE_PRIVATE_H + +BOOL WLog_DataMessage_Write(const char* filename, const void* data, size_t length); + +#endif /* WINPR_WLOG_DATA_MESSAGE_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/FileAppender.c b/winpr/libwinpr/utils/wlog/FileAppender.c new file mode 100644 index 0000000..7afc658 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/FileAppender.c @@ -0,0 +1,287 @@ +/** + * 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 "FileAppender.h" +#include "Message.h" + +#include <winpr/crt.h> +#include <winpr/environment.h> +#include <winpr/file.h> +#include <winpr/path.h> + +typedef struct +{ + WLOG_APPENDER_COMMON(); + + char* FileName; + char* FilePath; + char* FullFileName; + FILE* FileDescriptor; +} wLogFileAppender; + +static BOOL WLog_FileAppender_SetOutputFileName(wLogFileAppender* appender, const char* filename) +{ + appender->FileName = _strdup(filename); + + if (!appender->FileName) + return FALSE; + + return TRUE; +} + +static BOOL WLog_FileAppender_SetOutputFilePath(wLogFileAppender* appender, const char* filepath) +{ + appender->FilePath = _strdup(filepath); + + if (!appender->FilePath) + return FALSE; + + return TRUE; +} + +static BOOL WLog_FileAppender_Open(wLog* log, wLogAppender* appender) +{ + wLogFileAppender* fileAppender = NULL; + + if (!log || !appender) + return FALSE; + + fileAppender = (wLogFileAppender*)appender; + + if (!fileAppender->FilePath) + { + fileAppender->FilePath = GetKnownSubPath(KNOWN_PATH_TEMP, "wlog"); + + if (!fileAppender->FilePath) + return FALSE; + } + + if (!fileAppender->FileName) + { + fileAppender->FileName = (char*)malloc(MAX_PATH); + + if (!fileAppender->FileName) + return FALSE; + + sprintf_s(fileAppender->FileName, MAX_PATH, "%" PRIu32 ".log", GetCurrentProcessId()); + } + + if (!fileAppender->FullFileName) + { + fileAppender->FullFileName = + GetCombinedPath(fileAppender->FilePath, fileAppender->FileName); + + if (!fileAppender->FullFileName) + return FALSE; + } + + if (!winpr_PathFileExists(fileAppender->FilePath)) + { + if (!winpr_PathMakePath(fileAppender->FilePath, 0)) + return FALSE; + + UnixChangeFileMode(fileAppender->FilePath, 0xFFFF); + } + + fileAppender->FileDescriptor = winpr_fopen(fileAppender->FullFileName, "a+"); + + if (!fileAppender->FileDescriptor) + return FALSE; + + return TRUE; +} + +static BOOL WLog_FileAppender_Close(wLog* log, wLogAppender* appender) +{ + wLogFileAppender* fileAppender = NULL; + + if (!log || !appender) + return FALSE; + + fileAppender = (wLogFileAppender*)appender; + + if (!fileAppender->FileDescriptor) + return TRUE; + + fclose(fileAppender->FileDescriptor); + fileAppender->FileDescriptor = NULL; + return TRUE; +} + +static BOOL WLog_FileAppender_WriteMessage(wLog* log, wLogAppender* appender, wLogMessage* message) +{ + FILE* fp = NULL; + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + wLogFileAppender* fileAppender = NULL; + + if (!log || !appender || !message) + return FALSE; + + fileAppender = (wLogFileAppender*)appender; + fp = fileAppender->FileDescriptor; + + if (!fp) + return FALSE; + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + fprintf(fp, "%s%s\n", message->PrefixString, message->TextString); + fflush(fp); /* slow! */ + return TRUE; +} + +static int g_DataId = 0; + +static BOOL WLog_FileAppender_WriteDataMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + int DataId = 0; + char* FullFileName = NULL; + + if (!log || !appender || !message) + return FALSE; + + DataId = g_DataId++; + FullFileName = WLog_Message_GetOutputFileName(DataId, "dat"); + WLog_DataMessage_Write(FullFileName, message->Data, message->Length); + free(FullFileName); + return TRUE; +} + +static int g_ImageId = 0; + +static BOOL WLog_FileAppender_WriteImageMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + int ImageId = 0; + char* FullFileName = NULL; + + if (!log || !appender || !message) + return FALSE; + + ImageId = g_ImageId++; + FullFileName = WLog_Message_GetOutputFileName(ImageId, "bmp"); + WLog_ImageMessage_Write(FullFileName, message->ImageData, message->ImageWidth, + message->ImageHeight, message->ImageBpp); + free(FullFileName); + return TRUE; +} + +static BOOL WLog_FileAppender_Set(wLogAppender* appender, const char* setting, void* value) +{ + wLogFileAppender* fileAppender = (wLogFileAppender*)appender; + + /* Just check the value string is not empty */ + if (!value || (strnlen(value, 2) == 0)) + return FALSE; + + if (!strcmp("outputfilename", setting)) + return WLog_FileAppender_SetOutputFileName(fileAppender, (const char*)value); + + if (!strcmp("outputfilepath", setting)) + return WLog_FileAppender_SetOutputFilePath(fileAppender, (const char*)value); + + return FALSE; +} + +static void WLog_FileAppender_Free(wLogAppender* appender) +{ + wLogFileAppender* fileAppender = NULL; + + if (appender) + { + fileAppender = (wLogFileAppender*)appender; + free(fileAppender->FileName); + free(fileAppender->FilePath); + free(fileAppender->FullFileName); + free(fileAppender); + } +} + +wLogAppender* WLog_FileAppender_New(wLog* log) +{ + LPSTR env = NULL; + LPCSTR name = NULL; + DWORD nSize = 0; + wLogFileAppender* FileAppender = NULL; + FileAppender = (wLogFileAppender*)calloc(1, sizeof(wLogFileAppender)); + + if (!FileAppender) + return NULL; + + FileAppender->Type = WLOG_APPENDER_FILE; + FileAppender->Open = WLog_FileAppender_Open; + FileAppender->Close = WLog_FileAppender_Close; + FileAppender->WriteMessage = WLog_FileAppender_WriteMessage; + FileAppender->WriteDataMessage = WLog_FileAppender_WriteDataMessage; + FileAppender->WriteImageMessage = WLog_FileAppender_WriteImageMessage; + FileAppender->Free = WLog_FileAppender_Free; + FileAppender->Set = WLog_FileAppender_Set; + name = "WLOG_FILEAPPENDER_OUTPUT_FILE_PATH"; + nSize = GetEnvironmentVariableA(name, NULL, 0); + + if (nSize) + { + BOOL status = 0; + env = (LPSTR)malloc(nSize); + + if (!env) + goto error_free; + + if (GetEnvironmentVariableA(name, env, nSize) != nSize - 1) + { + free(env); + goto error_free; + } + + status = WLog_FileAppender_SetOutputFilePath(FileAppender, env); + free(env); + + if (!status) + goto error_free; + } + + name = "WLOG_FILEAPPENDER_OUTPUT_FILE_NAME"; + nSize = GetEnvironmentVariableA(name, NULL, 0); + + if (nSize) + { + BOOL status = FALSE; + env = (LPSTR)malloc(nSize); + + if (!env) + goto error_output_file_name; + + if (GetEnvironmentVariableA(name, env, nSize) == nSize - 1) + status = WLog_FileAppender_SetOutputFileName(FileAppender, env); + free(env); + + if (!status) + goto error_output_file_name; + } + + return (wLogAppender*)FileAppender; +error_output_file_name: + free(FileAppender->FilePath); +error_free: + free(FileAppender); + return NULL; +} diff --git a/winpr/libwinpr/utils/wlog/FileAppender.h b/winpr/libwinpr/utils/wlog/FileAppender.h new file mode 100644 index 0000000..8938488 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/FileAppender.h @@ -0,0 +1,28 @@ +/** + * 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. + */ + +#ifndef WINPR_WLOG_FILE_APPENDER_PRIVATE_H +#define WINPR_WLOG_FILE_APPENDER_PRIVATE_H + +#include "wlog.h" + +WINPR_ATTR_MALLOC(WLog_Appender_Free, 2) +wLogAppender* WLog_FileAppender_New(wLog* log); + +#endif /* WINPR_WLOG_FILE_APPENDER_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/ImageMessage.c b/winpr/libwinpr/utils/wlog/ImageMessage.c new file mode 100644 index 0000000..ce60032 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/ImageMessage.c @@ -0,0 +1,37 @@ +/** + * 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 "wlog.h" +#include <winpr/image.h> + +#include "ImageMessage.h" + +BOOL WLog_ImageMessage_Write(char* filename, void* data, size_t width, size_t height, size_t bpp) +{ + int status = 0; + + status = winpr_bitmap_write(filename, data, width, height, bpp); + + if (status < 0) + return FALSE; + + return TRUE; +} diff --git a/winpr/libwinpr/utils/wlog/ImageMessage.h b/winpr/libwinpr/utils/wlog/ImageMessage.h new file mode 100644 index 0000000..15ed81b --- /dev/null +++ b/winpr/libwinpr/utils/wlog/ImageMessage.h @@ -0,0 +1,25 @@ +/** + * 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. + */ + +#ifndef WINPR_WLOG_IMAGE_MESSAGE_PRIVATE_H +#define WINPR_WLOG_IMAGE_MESSAGE_PRIVATE_H + +BOOL WLog_ImageMessage_Write(char* filename, void* data, size_t width, size_t height, size_t bpp); + +#endif /* WINPR_WLOG_IMAGE_MESSAGE_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/JournaldAppender.c b/winpr/libwinpr/utils/wlog/JournaldAppender.c new file mode 100644 index 0000000..504108b --- /dev/null +++ b/winpr/libwinpr/utils/wlog/JournaldAppender.c @@ -0,0 +1,210 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright © 2015 Thincast Technologies GmbH + * Copyright © 2015 David FORT <contact@hardening-consulting.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 "JournaldAppender.h" + +#include <unistd.h> +#include <syslog.h> +#include <systemd/sd-journal.h> + +#include <winpr/crt.h> +#include <winpr/environment.h> + +typedef struct +{ + WLOG_APPENDER_COMMON(); + char* identifier; + FILE* stream; +} wLogJournaldAppender; + +static BOOL WLog_JournaldAppender_Open(wLog* log, wLogAppender* appender) +{ + int fd = 0; + wLogJournaldAppender* journaldAppender = NULL; + + if (!log || !appender) + return FALSE; + + journaldAppender = (wLogJournaldAppender*)appender; + if (journaldAppender->stream) + return TRUE; + + fd = sd_journal_stream_fd(journaldAppender->identifier, LOG_INFO, 1); + if (fd < 0) + return FALSE; + + journaldAppender->stream = fdopen(fd, "w"); + if (!journaldAppender->stream) + { + close(fd); + return FALSE; + } + + setbuffer(journaldAppender->stream, NULL, 0); + return TRUE; +} + +static BOOL WLog_JournaldAppender_Close(wLog* log, wLogAppender* appender) +{ + if (!log || !appender) + return FALSE; + + return TRUE; +} + +static BOOL WLog_JournaldAppender_WriteMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + char* formatStr = NULL; + wLogJournaldAppender* journaldAppender = NULL; + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + + if (!log || !appender || !message) + return FALSE; + + journaldAppender = (wLogJournaldAppender*)appender; + + switch (message->Level) + { + case WLOG_TRACE: + case WLOG_DEBUG: + formatStr = "<7>%s%s\n"; + break; + case WLOG_INFO: + formatStr = "<6>%s%s\n"; + break; + case WLOG_WARN: + formatStr = "<4>%s%s\n"; + break; + case WLOG_ERROR: + formatStr = "<3>%s%s\n"; + break; + case WLOG_FATAL: + formatStr = "<2>%s%s\n"; + break; + case WLOG_OFF: + return TRUE; + default: + fprintf(stderr, "%s: unknown level %" PRIu32 "\n", __func__, message->Level); + return FALSE; + } + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + + if (message->Level != WLOG_OFF) + fprintf(journaldAppender->stream, formatStr, message->PrefixString, message->TextString); + return TRUE; +} + +static BOOL WLog_JournaldAppender_WriteDataMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + if (!log || !appender || !message) + return FALSE; + + return TRUE; +} + +static BOOL WLog_JournaldAppender_WriteImageMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + if (!log || !appender || !message) + return FALSE; + + return TRUE; +} + +static BOOL WLog_JournaldAppender_Set(wLogAppender* appender, const char* setting, void* value) +{ + wLogJournaldAppender* journaldAppender = (wLogJournaldAppender*)appender; + + /* Just check the value string is not empty */ + if (!value || (strnlen(value, 2) == 0)) + return FALSE; + + if (strcmp("identifier", setting)) + return FALSE; + + /* If the stream is already open the identifier can't be changed */ + if (journaldAppender->stream) + return FALSE; + + if (journaldAppender->identifier) + free(journaldAppender->identifier); + + return ((journaldAppender->identifier = _strdup((const char*)value)) != NULL); +} + +static void WLog_JournaldAppender_Free(wLogAppender* appender) +{ + wLogJournaldAppender* journaldAppender = NULL; + if (appender) + { + journaldAppender = (wLogJournaldAppender*)appender; + if (journaldAppender->stream) + fclose(journaldAppender->stream); + free(journaldAppender->identifier); + free(journaldAppender); + } +} + +wLogAppender* WLog_JournaldAppender_New(wLog* log) +{ + wLogJournaldAppender* appender = NULL; + DWORD nSize = 0; + LPCSTR name = "WLOG_JOURNALD_ID"; + + appender = (wLogJournaldAppender*)calloc(1, sizeof(wLogJournaldAppender)); + if (!appender) + return NULL; + + appender->Type = WLOG_APPENDER_JOURNALD; + appender->Open = WLog_JournaldAppender_Open; + appender->Close = WLog_JournaldAppender_Close; + appender->WriteMessage = WLog_JournaldAppender_WriteMessage; + appender->WriteDataMessage = WLog_JournaldAppender_WriteDataMessage; + appender->WriteImageMessage = WLog_JournaldAppender_WriteImageMessage; + appender->Set = WLog_JournaldAppender_Set; + appender->Free = WLog_JournaldAppender_Free; + + nSize = GetEnvironmentVariableA(name, NULL, 0); + if (nSize) + { + appender->identifier = (LPSTR)malloc(nSize); + if (!appender->identifier) + goto error_open; + + if (GetEnvironmentVariableA(name, appender->identifier, nSize) != nSize - 1) + goto error_open; + + if (!WLog_JournaldAppender_Open(log, (wLogAppender*)appender)) + goto error_open; + } + + return (wLogAppender*)appender; + +error_open: + free(appender->identifier); + free(appender); + return NULL; +} diff --git a/winpr/libwinpr/utils/wlog/JournaldAppender.h b/winpr/libwinpr/utils/wlog/JournaldAppender.h new file mode 100644 index 0000000..49223c5 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/JournaldAppender.h @@ -0,0 +1,31 @@ +/** + * Copyright © 2015 Thincast Technologies GmbH + * Copyright © 2015 David FORT <contact@hardening-consulting.com> + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WINPR_LIBWINPR_UTILS_WLOG_JOURNALDAPPENDER_H_ +#define WINPR_LIBWINPR_UTILS_WLOG_JOURNALDAPPENDER_H_ + +#include "wlog.h" + +wLogAppender* WLog_JournaldAppender_New(wLog* log); + +#endif /* WINPR_LIBWINPR_UTILS_WLOG_JOURNALDAPPENDER_H_ */ diff --git a/winpr/libwinpr/utils/wlog/Layout.c b/winpr/libwinpr/utils/wlog/Layout.c new file mode 100644 index 0000000..188c15b --- /dev/null +++ b/winpr/libwinpr/utils/wlog/Layout.c @@ -0,0 +1,375 @@ +/** + * 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 <string.h> +#include <stdarg.h> + +#include <winpr/crt.h> +#include <winpr/assert.h> +#include <winpr/print.h> +#include <winpr/sysinfo.h> +#include <winpr/environment.h> + +#include "wlog.h" + +#include "Layout.h" + +#if defined __linux__ && !defined ANDROID +#include <unistd.h> +#include <sys/syscall.h> +#endif + +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +struct format_option_recurse; + +struct format_option +{ + const char* fmt; + size_t fmtlen; + const char* replace; + size_t replacelen; + const char* (*fkt)(void*); + void* arg; + const char* (*ext)(const struct format_option* opt, const char* str, size_t* preplacelen, + size_t* pskiplen); + struct format_option_recurse* recurse; +}; + +struct format_option_recurse +{ + struct format_option* options; + size_t nroptions; + wLog* log; + wLogLayout* layout; + wLogMessage* message; + char buffer[WLOG_MAX_PREFIX_SIZE]; +}; + +/** + * Log Layout + */ +WINPR_ATTR_FORMAT_ARG(3, 0) +static void WLog_PrintMessagePrefixVA(wLog* log, wLogMessage* message, + WINPR_FORMAT_ARG const char* format, va_list args) +{ + WINPR_ASSERT(message); + vsnprintf(message->PrefixString, WLOG_MAX_PREFIX_SIZE - 1, format, args); +} + +WINPR_ATTR_FORMAT_ARG(3, 4) +static void WLog_PrintMessagePrefix(wLog* log, wLogMessage* message, + WINPR_FORMAT_ARG const char* format, ...) +{ + va_list args; + va_start(args, format); + WLog_PrintMessagePrefixVA(log, message, format, args); + va_end(args); +} + +static const char* get_tid(void* arg) +{ + char* str = arg; + size_t tid = 0; +#if defined __linux__ && !defined ANDROID + /* On Linux we prefer to see the LWP id */ + tid = (size_t)syscall(SYS_gettid); +#else + tid = (size_t)GetCurrentThreadId(); +#endif + sprintf(str, "%08" PRIxz, tid); + return str; +} + +static BOOL log_invalid_fmt(const char* what) +{ + fprintf(stderr, "Invalid format string '%s'\n", what); + return FALSE; +} + +static BOOL check_and_log_format_size(char* format, size_t size, size_t index, size_t add) +{ + /* format string must be '\0' terminated, so abort at size - 1 */ + if (index + add + 1 >= size) + { + fprintf(stderr, + "Format string too long ['%s', max %" PRIuz ", used %" PRIuz ", adding %" PRIuz + "]\n", + format, size, index, add); + return FALSE; + } + return TRUE; +} + +static int opt_compare_fn(const void* a, const void* b) +{ + const char* what = a; + const struct format_option* opt = b; + if (!opt) + return -1; + return strncmp(what, opt->fmt, opt->fmtlen); +} + +static BOOL replace_format_string(const char* FormatString, struct format_option_recurse* recurse, + char* format, size_t formatlen); + +static const char* skip_if_null(const struct format_option* opt, const char* fmt, + size_t* preplacelen, size_t* pskiplen) +{ + WINPR_ASSERT(opt); + WINPR_ASSERT(fmt); + WINPR_ASSERT(preplacelen); + WINPR_ASSERT(pskiplen); + + *preplacelen = 0; + *pskiplen = 0; + + const char* str = &fmt[opt->fmtlen]; /* Skip first %{ from string */ + const char* end = strstr(str, opt->replace); + if (!end) + return NULL; + *pskiplen = end - fmt + opt->replacelen; + + if (!opt->arg) + return NULL; + + const size_t replacelen = end - str; + + char buffer[WLOG_MAX_PREFIX_SIZE] = { 0 }; + memcpy(buffer, str, MIN(replacelen, ARRAYSIZE(buffer) - 1)); + + if (!replace_format_string(buffer, opt->recurse, opt->recurse->buffer, + ARRAYSIZE(opt->recurse->buffer))) + return NULL; + + *preplacelen = strnlen(opt->recurse->buffer, ARRAYSIZE(opt->recurse->buffer)); + return opt->recurse->buffer; +} + +static BOOL replace_format_string(const char* FormatString, struct format_option_recurse* recurse, + char* format, size_t formatlen) +{ + WINPR_ASSERT(FormatString); + WINPR_ASSERT(recurse); + + size_t index = 0; + + while (*FormatString) + { + const struct format_option* opt = + bsearch(FormatString, recurse->options, recurse->nroptions, + sizeof(struct format_option), opt_compare_fn); + if (opt) + { + size_t replacelen = opt->replacelen; + size_t fmtlen = opt->fmtlen; + const char* replace = opt->replace; + const void* arg = opt->arg; + + if (opt->ext) + replace = opt->ext(opt, FormatString, &replacelen, &fmtlen); + if (opt->fkt) + arg = opt->fkt(opt->arg); + + if (replace && (replacelen > 0)) + { + const int rc = _snprintf(&format[index], formatlen - index, replace, arg); + if (rc < 0) + return FALSE; + if (!check_and_log_format_size(format, formatlen, index, rc)) + return FALSE; + index += rc; + } + FormatString += fmtlen; + } + else + { + /* Unknown format string */ + if (*FormatString == '%') + return log_invalid_fmt(FormatString); + + if (!check_and_log_format_size(format, formatlen, index, 1)) + return FALSE; + format[index++] = *FormatString++; + } + } + + if (!check_and_log_format_size(format, formatlen, index, 0)) + return FALSE; + return TRUE; +} + +BOOL WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout, wLogMessage* message) +{ + char format[WLOG_MAX_PREFIX_SIZE] = { 0 }; + + WINPR_ASSERT(layout); + WINPR_ASSERT(message); + + char tid[32] = { 0 }; + SYSTEMTIME localTime = { 0 }; + GetLocalTime(&localTime); + + struct format_option_recurse recurse = { + .options = NULL, .nroptions = 0, .log = log, .layout = layout, .message = message + }; + +#define ENTRY(x) x, sizeof(x) - 1 + struct format_option options[] = { + { ENTRY("%ctx"), ENTRY("%s"), log->custom, log->context, NULL, &recurse }, /* log context */ + { ENTRY("%dw"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wDayOfWeek, NULL, + &recurse }, /* day of week */ + { ENTRY("%dy"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wDay, NULL, + &recurse }, /* day of year */ + { ENTRY("%fl"), ENTRY("%s"), NULL, (void*)message->FileName, NULL, &recurse }, /* file */ + { ENTRY("%fn"), ENTRY("%s"), NULL, (void*)message->FunctionName, NULL, + &recurse }, /* function */ + { ENTRY("%hr"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wHour, NULL, + &recurse }, /* hours */ + { ENTRY("%ln"), ENTRY("%" PRIuz), NULL, (void*)(size_t)message->LineNumber, NULL, + &recurse }, /* line number */ + { ENTRY("%lv"), ENTRY("%s"), NULL, (void*)WLOG_LEVELS[message->Level], NULL, + &recurse }, /* log level */ + { ENTRY("%mi"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wMinute, NULL, + &recurse }, /* minutes */ + { ENTRY("%ml"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wMilliseconds, NULL, + &recurse }, /* milliseconds */ + { ENTRY("%mn"), ENTRY("%s"), NULL, log->Name, NULL, &recurse }, /* module name */ + { ENTRY("%mo"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wMonth, NULL, + &recurse }, /* month */ + { ENTRY("%pid"), ENTRY("%u"), NULL, (void*)(size_t)GetCurrentProcessId(), NULL, + &recurse }, /* process id */ + { ENTRY("%se"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wSecond, NULL, + &recurse }, /* seconds */ + { ENTRY("%tid"), ENTRY("%s"), get_tid, tid, NULL, &recurse }, /* thread id */ + { ENTRY("%yr"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wYear, NULL, + &recurse }, /* year */ + { ENTRY("%{"), ENTRY("%}"), NULL, log->context, skip_if_null, + &recurse }, /* skip if no context */ + }; + + recurse.options = options; + recurse.nroptions = ARRAYSIZE(options); + + if (!replace_format_string(layout->FormatString, &recurse, format, ARRAYSIZE(format))) + return FALSE; + + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_FORMAT_SECURITY + + WLog_PrintMessagePrefix(log, message, format); + + WINPR_PRAGMA_DIAG_POP + + return TRUE; +} + +wLogLayout* WLog_GetLogLayout(wLog* log) +{ + wLogAppender* appender = NULL; + appender = WLog_GetLogAppender(log); + return appender->Layout; +} + +BOOL WLog_Layout_SetPrefixFormat(wLog* log, wLogLayout* layout, const char* format) +{ + free(layout->FormatString); + layout->FormatString = NULL; + + if (format) + { + layout->FormatString = _strdup(format); + + if (!layout->FormatString) + return FALSE; + } + + return TRUE; +} + +wLogLayout* WLog_Layout_New(wLog* log) +{ + LPCSTR prefix = "WLOG_PREFIX"; + DWORD nSize = 0; + char* env = NULL; + wLogLayout* layout = NULL; + layout = (wLogLayout*)calloc(1, sizeof(wLogLayout)); + + if (!layout) + return NULL; + + nSize = GetEnvironmentVariableA(prefix, NULL, 0); + + if (nSize) + { + env = (LPSTR)malloc(nSize); + + if (!env) + { + free(layout); + return NULL; + } + + if (GetEnvironmentVariableA(prefix, env, nSize) != nSize - 1) + { + free(env); + free(layout); + return NULL; + } + } + + if (env) + layout->FormatString = env; + else + { +#ifdef ANDROID + layout->FormatString = _strdup("[pid=%pid:tid=%tid] - [%fn]%{[%ctx]%}: "); +#else + layout->FormatString = + _strdup("[%hr:%mi:%se:%ml] [%pid:%tid] [%lv][%mn] - [%fn]%{[%ctx]%}: "); +#endif + + if (!layout->FormatString) + { + free(layout); + return NULL; + } + } + + return layout; +} + +void WLog_Layout_Free(wLog* log, wLogLayout* layout) +{ + if (layout) + { + if (layout->FormatString) + { + free(layout->FormatString); + layout->FormatString = NULL; + } + + free(layout); + } +} diff --git a/winpr/libwinpr/utils/wlog/Layout.h b/winpr/libwinpr/utils/wlog/Layout.h new file mode 100644 index 0000000..8698077 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/Layout.h @@ -0,0 +1,41 @@ +/** + * 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. + */ + +#ifndef WINPR_WLOG_LAYOUT_PRIVATE_H +#define WINPR_WLOG_LAYOUT_PRIVATE_H + +#include "wlog.h" + +/** + * Log Layout + */ + +struct s_wLogLayout +{ + DWORD Type; + + LPSTR FormatString; +}; + +void WLog_Layout_Free(wLog* log, wLogLayout* layout); + +WINPR_ATTR_MALLOC(WLog_Layout_Free, 2) +wLogLayout* WLog_Layout_New(wLog* log); + +#endif /* WINPR_WLOG_LAYOUT_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/Message.c b/winpr/libwinpr/utils/wlog/Message.c new file mode 100644 index 0000000..bedb5ed --- /dev/null +++ b/winpr/libwinpr/utils/wlog/Message.c @@ -0,0 +1,64 @@ +/** + * 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 <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/file.h> + +#include "wlog.h" + +#include "Message.h" + +char* WLog_Message_GetOutputFileName(int id, const char* ext) +{ + DWORD ProcessId = 0; + char* FilePath = NULL; + char* FileName = NULL; + char* FullFileName = NULL; + + if (!(FileName = (char*)malloc(256))) + return NULL; + + FilePath = GetKnownSubPath(KNOWN_PATH_TEMP, "wlog"); + + if (!winpr_PathFileExists(FilePath)) + { + if (!winpr_PathMakePath(FilePath, NULL)) + { + free(FileName); + free(FilePath); + return NULL; + } + } + + ProcessId = GetCurrentProcessId(); + if (id >= 0) + sprintf_s(FileName, 256, "%" PRIu32 "-%d.%s", ProcessId, id, ext); + else + sprintf_s(FileName, 256, "%" PRIu32 ".%s", ProcessId, ext); + + FullFileName = GetCombinedPath(FilePath, FileName); + + free(FileName); + free(FilePath); + + return FullFileName; +} diff --git a/winpr/libwinpr/utils/wlog/Message.h b/winpr/libwinpr/utils/wlog/Message.h new file mode 100644 index 0000000..c65b33c --- /dev/null +++ b/winpr/libwinpr/utils/wlog/Message.h @@ -0,0 +1,29 @@ +/** + * 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. + */ + +#ifndef WINPR_WLOG_MESSAGE_PRIVATE_H +#define WINPR_WLOG_MESSAGE_PRIVATE_H + +#include "DataMessage.h" +#include "ImageMessage.h" +#include "PacketMessage.h" + +char* WLog_Message_GetOutputFileName(int id, const char* ext); + +#endif /* WINPR_WLOG_MESSAGE_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/PacketMessage.c b/winpr/libwinpr/utils/wlog/PacketMessage.c new file mode 100644 index 0000000..cc1c812 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/PacketMessage.c @@ -0,0 +1,487 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.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 "wlog.h" + +#include "PacketMessage.h" + +#include <winpr/wtypes.h> +#include <winpr/crt.h> +#include <winpr/file.h> +#include <winpr/stream.h> + +#include "../../log.h" +#define TAG WINPR_TAG("utils.wlog") + +#ifndef _WIN32 +#include <sys/time.h> +#else +#include <time.h> +#include <sys/timeb.h> +#include <winpr/windows.h> + +static int gettimeofday(struct timeval* tp, void* tz) +{ + struct _timeb timebuffer; + _ftime(&timebuffer); + tp->tv_sec = (long)timebuffer.time; + tp->tv_usec = timebuffer.millitm * 1000; + return 0; +} +#endif + +static BOOL Pcap_Read_Header(wPcap* pcap, wPcapHeader* header) +{ + if (pcap && pcap->fp && fread((void*)header, sizeof(wPcapHeader), 1, pcap->fp) == 1) + return TRUE; + return FALSE; +} + +/* currently unused code */ +#if 0 +static BOOL Pcap_Read_RecordHeader(wPcap* pcap, wPcapRecordHeader* record) +{ + if (pcap && pcap->fp && (fread((void*) record, sizeof(wPcapRecordHeader), 1, pcap->fp) == 1)) + return TRUE; + return FALSE; +} + +static BOOL Pcap_Read_Record(wPcap* pcap, wPcapRecord* record) +{ + if (pcap && pcap->fp) + { + if (!Pcap_Read_RecordHeader(pcap, &record->header)) + return FALSE; + record->length = record->header.incl_len; + record->data = malloc(record->length); + if (!record->data) + return FALSE; + if (fread(record->data, record->length, 1, pcap->fp) != 1) + { + free(record->data); + record->length = 0; + record->data = NULL; + return FALSE; + } + } + return TRUE; +} + +static BOOL Pcap_Add_Record(wPcap* pcap, void* data, UINT32 length) +{ + wPcapRecord* record; + struct timeval tp; + + if (!pcap->tail) + { + pcap->tail = (wPcapRecord*) calloc(1, sizeof(wPcapRecord)); + if (!pcap->tail) + return FALSE; + pcap->head = pcap->tail; + pcap->record = pcap->head; + record = pcap->tail; + } + else + { + record = (wPcapRecord*) calloc(1, sizeof(wPcapRecord)); + if (!record) + return FALSE; + pcap->tail->next = record; + pcap->tail = record; + } + + if (!pcap->record) + pcap->record = record; + + record->data = data; + record->length = length; + record->header.incl_len = length; + record->header.orig_len = length; + gettimeofday(&tp, 0); + record->header.ts_sec = tp.tv_sec; + record->header.ts_usec = tp.tv_usec; + return TRUE; +} + +static BOOL Pcap_HasNext_Record(wPcap* pcap) +{ + if (pcap->file_size - (_ftelli64(pcap->fp)) <= 16) + return FALSE; + + return TRUE; +} + +static BOOL Pcap_GetNext_RecordHeader(wPcap* pcap, wPcapRecord* record) +{ + if (!Pcap_HasNext_Record(pcap) || !Pcap_Read_RecordHeader(pcap, &record->header)) + return FALSE; + + record->length = record->header.incl_len; + return TRUE; +} + +static BOOL Pcap_GetNext_RecordContent(wPcap* pcap, wPcapRecord* record) +{ + if (pcap && pcap->fp && fread(record->data, record->length, 1, pcap->fp) == 1) + return TRUE; + + return FALSE; +} + +static BOOL Pcap_GetNext_Record(wPcap* pcap, wPcapRecord* record) +{ + if (!Pcap_HasNext_Record(pcap)) + return FALSE; + + return Pcap_Read_Record(pcap, record); +} +#endif + +static BOOL Pcap_Write_Header(wPcap* pcap, wPcapHeader* header) +{ + if (pcap && pcap->fp && fwrite((void*)header, sizeof(wPcapHeader), 1, pcap->fp) == 1) + return TRUE; + return FALSE; +} + +static BOOL Pcap_Write_RecordHeader(wPcap* pcap, wPcapRecordHeader* record) +{ + if (pcap && pcap->fp && fwrite((void*)record, sizeof(wPcapRecordHeader), 1, pcap->fp) == 1) + return TRUE; + return FALSE; +} + +static BOOL Pcap_Write_RecordContent(wPcap* pcap, wPcapRecord* record) +{ + if (pcap && pcap->fp && fwrite(record->data, record->length, 1, pcap->fp) == 1) + return TRUE; + return FALSE; +} + +static BOOL Pcap_Write_Record(wPcap* pcap, wPcapRecord* record) +{ + return Pcap_Write_RecordHeader(pcap, &record->header) && Pcap_Write_RecordContent(pcap, record); +} + +wPcap* Pcap_Open(char* name, BOOL write) +{ + wPcap* pcap = NULL; + FILE* pcap_fp = winpr_fopen(name, write ? "w+b" : "rb"); + + if (!pcap_fp) + { + WLog_ERR(TAG, "opening pcap file"); + return NULL; + } + + pcap = (wPcap*)calloc(1, sizeof(wPcap)); + + if (!pcap) + goto out_fail; + + pcap->name = name; + pcap->write = write; + pcap->record_count = 0; + pcap->fp = pcap_fp; + + if (write) + { + pcap->header.magic_number = PCAP_MAGIC_NUMBER; + pcap->header.version_major = 2; + pcap->header.version_minor = 4; + pcap->header.thiszone = 0; + pcap->header.sigfigs = 0; + pcap->header.snaplen = 0xFFFFFFFF; + pcap->header.network = 1; /* ethernet */ + if (!Pcap_Write_Header(pcap, &pcap->header)) + goto out_fail; + } + else + { + if (_fseeki64(pcap->fp, 0, SEEK_END) < 0) + goto out_fail; + pcap->file_size = (SSIZE_T)_ftelli64(pcap->fp); + if (pcap->file_size < 0) + goto out_fail; + if (_fseeki64(pcap->fp, 0, SEEK_SET) < 0) + goto out_fail; + if (!Pcap_Read_Header(pcap, &pcap->header)) + goto out_fail; + } + + return pcap; + +out_fail: + if (pcap_fp) + fclose(pcap_fp); + free(pcap); + return NULL; +} + +void Pcap_Flush(wPcap* pcap) +{ + if (!pcap || !pcap->fp) + return; + + while (pcap->record) + { + if (!Pcap_Write_Record(pcap, pcap->record)) + return; + pcap->record = pcap->record->next; + } + + fflush(pcap->fp); + return; +} + +void Pcap_Close(wPcap* pcap) +{ + if (!pcap || !pcap->fp) + return; + + Pcap_Flush(pcap); + fclose(pcap->fp); + free(pcap); +} + +static BOOL WLog_PacketMessage_Write_EthernetHeader(wPcap* pcap, wEthernetHeader* ethernet) +{ + wStream* s = NULL; + wStream sbuffer = { 0 }; + BYTE buffer[14] = { 0 }; + BOOL ret = TRUE; + + if (!pcap || !pcap->fp || !ethernet) + return FALSE; + + s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer)); + if (!s) + return FALSE; + Stream_Write(s, ethernet->Destination, 6); + Stream_Write(s, ethernet->Source, 6); + Stream_Write_UINT16_BE(s, ethernet->Type); + if (fwrite(buffer, sizeof(buffer), 1, pcap->fp) != 1) + ret = FALSE; + + return ret; +} + +static UINT16 IPv4Checksum(BYTE* ipv4, int length) +{ + UINT16 tmp16 = 0; + long checksum = 0; + + while (length > 1) + { + tmp16 = *((UINT16*)ipv4); + checksum += tmp16; + length -= 2; + ipv4 += 2; + } + + if (length > 0) + checksum += *ipv4; + + while (checksum >> 16) + checksum = (checksum & 0xFFFF) + (checksum >> 16); + + return (UINT16)(~checksum); +} + +static BOOL WLog_PacketMessage_Write_IPv4Header(wPcap* pcap, wIPv4Header* ipv4) +{ + wStream* s = NULL; + wStream sbuffer = { 0 }; + BYTE buffer[20] = { 0 }; + int ret = TRUE; + + if (!pcap || !pcap->fp || !ipv4) + return FALSE; + + s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer)); + if (!s) + return FALSE; + Stream_Write_UINT8(s, (ipv4->Version << 4) | ipv4->InternetHeaderLength); + Stream_Write_UINT8(s, ipv4->TypeOfService); + Stream_Write_UINT16_BE(s, ipv4->TotalLength); + Stream_Write_UINT16_BE(s, ipv4->Identification); + Stream_Write_UINT16_BE(s, (ipv4->InternetProtocolFlags << 13) | ipv4->FragmentOffset); + Stream_Write_UINT8(s, ipv4->TimeToLive); + Stream_Write_UINT8(s, ipv4->Protocol); + Stream_Write_UINT16(s, ipv4->HeaderChecksum); + Stream_Write_UINT32_BE(s, ipv4->SourceAddress); + Stream_Write_UINT32_BE(s, ipv4->DestinationAddress); + ipv4->HeaderChecksum = IPv4Checksum((BYTE*)buffer, 20); + Stream_Rewind(s, 10); + Stream_Write_UINT16(s, ipv4->HeaderChecksum); + + if (fwrite(buffer, sizeof(buffer), 1, pcap->fp) != 1) + ret = FALSE; + + return ret; +} + +static BOOL WLog_PacketMessage_Write_TcpHeader(wPcap* pcap, wTcpHeader* tcp) +{ + wStream* s = NULL; + wStream sbuffer = { 0 }; + BYTE buffer[20] = { 0 }; + BOOL ret = TRUE; + + if (!pcap || !pcap->fp || !tcp) + return FALSE; + + s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer)); + if (!s) + return FALSE; + Stream_Write_UINT16_BE(s, tcp->SourcePort); + Stream_Write_UINT16_BE(s, tcp->DestinationPort); + Stream_Write_UINT32_BE(s, tcp->SequenceNumber); + Stream_Write_UINT32_BE(s, tcp->AcknowledgementNumber); + Stream_Write_UINT8(s, (tcp->Offset << 4) | tcp->Reserved); + Stream_Write_UINT8(s, tcp->TcpFlags); + Stream_Write_UINT16_BE(s, tcp->Window); + Stream_Write_UINT16_BE(s, tcp->Checksum); + Stream_Write_UINT16_BE(s, tcp->UrgentPointer); + + if (pcap->fp) + { + if (fwrite(buffer, sizeof(buffer), 1, pcap->fp) != 1) + ret = FALSE; + } + + return ret; +} + +static UINT32 g_InboundSequenceNumber = 0; +static UINT32 g_OutboundSequenceNumber = 0; + +BOOL WLog_PacketMessage_Write(wPcap* pcap, void* data, size_t length, DWORD flags) +{ + wTcpHeader tcp; + wIPv4Header ipv4; + struct timeval tp; + wPcapRecord record; + wEthernetHeader ethernet; + ethernet.Type = 0x0800; + + if (!pcap || !pcap->fp) + return FALSE; + + if (flags & WLOG_PACKET_OUTBOUND) + { + /* 00:15:5D:01:64:04 */ + ethernet.Source[0] = 0x00; + ethernet.Source[1] = 0x15; + ethernet.Source[2] = 0x5D; + ethernet.Source[3] = 0x01; + ethernet.Source[4] = 0x64; + ethernet.Source[5] = 0x04; + /* 00:15:5D:01:64:01 */ + ethernet.Destination[0] = 0x00; + ethernet.Destination[1] = 0x15; + ethernet.Destination[2] = 0x5D; + ethernet.Destination[3] = 0x01; + ethernet.Destination[4] = 0x64; + ethernet.Destination[5] = 0x01; + } + else + { + /* 00:15:5D:01:64:01 */ + ethernet.Source[0] = 0x00; + ethernet.Source[1] = 0x15; + ethernet.Source[2] = 0x5D; + ethernet.Source[3] = 0x01; + ethernet.Source[4] = 0x64; + ethernet.Source[5] = 0x01; + /* 00:15:5D:01:64:04 */ + ethernet.Destination[0] = 0x00; + ethernet.Destination[1] = 0x15; + ethernet.Destination[2] = 0x5D; + ethernet.Destination[3] = 0x01; + ethernet.Destination[4] = 0x64; + ethernet.Destination[5] = 0x04; + } + + ipv4.Version = 4; + ipv4.InternetHeaderLength = 5; + ipv4.TypeOfService = 0; + ipv4.TotalLength = (UINT16)(length + 20 + 20); + ipv4.Identification = 0; + ipv4.InternetProtocolFlags = 0x02; + ipv4.FragmentOffset = 0; + ipv4.TimeToLive = 128; + ipv4.Protocol = 6; /* TCP */ + ipv4.HeaderChecksum = 0; + + if (flags & WLOG_PACKET_OUTBOUND) + { + ipv4.SourceAddress = 0xC0A80196; /* 192.168.1.150 */ + ipv4.DestinationAddress = 0x4A7D64C8; /* 74.125.100.200 */ + } + else + { + ipv4.SourceAddress = 0x4A7D64C8; /* 74.125.100.200 */ + ipv4.DestinationAddress = 0xC0A80196; /* 192.168.1.150 */ + } + + tcp.SourcePort = 3389; + tcp.DestinationPort = 3389; + + if (flags & WLOG_PACKET_OUTBOUND) + { + tcp.SequenceNumber = g_OutboundSequenceNumber; + tcp.AcknowledgementNumber = g_InboundSequenceNumber; + g_OutboundSequenceNumber += length; + } + else + { + tcp.SequenceNumber = g_InboundSequenceNumber; + tcp.AcknowledgementNumber = g_OutboundSequenceNumber; + g_InboundSequenceNumber += length; + } + + tcp.Offset = 5; + tcp.Reserved = 0; + tcp.TcpFlags = 0x0018; + tcp.Window = 0x7FFF; + tcp.Checksum = 0; + tcp.UrgentPointer = 0; + record.data = data; + record.length = length; + const size_t offset = 14 + 20 + 20; + WINPR_ASSERT(record.length <= UINT32_MAX - offset); + record.header.incl_len = (UINT32)record.length + offset; + record.header.orig_len = (UINT32)record.length + offset; + record.next = NULL; + gettimeofday(&tp, 0); + record.header.ts_sec = tp.tv_sec; + record.header.ts_usec = tp.tv_usec; + if (!Pcap_Write_RecordHeader(pcap, &record.header) || + !WLog_PacketMessage_Write_EthernetHeader(pcap, ðernet) || + !WLog_PacketMessage_Write_IPv4Header(pcap, &ipv4) || + !WLog_PacketMessage_Write_TcpHeader(pcap, &tcp) || !Pcap_Write_RecordContent(pcap, &record)) + return FALSE; + fflush(pcap->fp); + return TRUE; +} diff --git a/winpr/libwinpr/utils/wlog/PacketMessage.h b/winpr/libwinpr/utils/wlog/PacketMessage.h new file mode 100644 index 0000000..088ee8f --- /dev/null +++ b/winpr/libwinpr/utils/wlog/PacketMessage.h @@ -0,0 +1,111 @@ +/** + * 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. + */ + +#ifndef WINPR_WLOG_PACKET_MESSAGE_PRIVATE_H +#define WINPR_WLOG_PACKET_MESSAGE_PRIVATE_H + +#include "wlog.h" + +#define PCAP_MAGIC_NUMBER 0xA1B2C3D4 + +typedef struct +{ + UINT32 magic_number; /* magic number */ + UINT16 version_major; /* major version number */ + UINT16 version_minor; /* minor version number */ + INT32 thiszone; /* GMT to local correction */ + UINT32 sigfigs; /* accuracy of timestamps */ + UINT32 snaplen; /* max length of captured packets, in octets */ + UINT32 network; /* data link type */ +} wPcapHeader; + +typedef struct +{ + UINT32 ts_sec; /* timestamp seconds */ + UINT32 ts_usec; /* timestamp microseconds */ + UINT32 incl_len; /* number of octets of packet saved in file */ + UINT32 orig_len; /* actual length of packet */ +} wPcapRecordHeader; + +typedef struct s_wPcapRecort +{ + wPcapRecordHeader header; + void* data; + size_t length; + struct s_wPcapRecort* next; +} wPcapRecord; + +typedef struct +{ + FILE* fp; + char* name; + BOOL write; + SSIZE_T file_size; + size_t record_count; + wPcapHeader header; + wPcapRecord* head; + wPcapRecord* tail; + wPcapRecord* record; +} wPcap; + +wPcap* Pcap_Open(char* name, BOOL write); +void Pcap_Close(wPcap* pcap); + +void Pcap_Flush(wPcap* pcap); + +typedef struct +{ + BYTE Destination[6]; + BYTE Source[6]; + UINT16 Type; +} wEthernetHeader; + +typedef struct +{ + BYTE Version; + BYTE InternetHeaderLength; + BYTE TypeOfService; + UINT16 TotalLength; + UINT16 Identification; + BYTE InternetProtocolFlags; + UINT16 FragmentOffset; + BYTE TimeToLive; + BYTE Protocol; + UINT16 HeaderChecksum; + UINT32 SourceAddress; + UINT32 DestinationAddress; +} wIPv4Header; + +typedef struct +{ + UINT16 SourcePort; + UINT16 DestinationPort; + UINT32 SequenceNumber; + UINT32 AcknowledgementNumber; + BYTE Offset; + BYTE Reserved; + BYTE TcpFlags; + UINT16 Window; + UINT16 Checksum; + UINT16 UrgentPointer; +} wTcpHeader; + +BOOL WLog_PacketMessage_Write(wPcap* pcap, void* data, size_t length, DWORD flags); + +#endif /* WINPR_WLOG_PACKET_MESSAGE_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/SyslogAppender.c b/winpr/libwinpr/utils/wlog/SyslogAppender.c new file mode 100644 index 0000000..73baade --- /dev/null +++ b/winpr/libwinpr/utils/wlog/SyslogAppender.c @@ -0,0 +1,137 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright © 2015 Thincast Technologies GmbH + * Copyright © 2015 David FORT <contact@hardening-consulting.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 "SyslogAppender.h" +#include <syslog.h> + +typedef struct +{ + WLOG_APPENDER_COMMON(); +} wLogSyslogAppender; + +static int getSyslogLevel(DWORD level) +{ + switch (level) + { + case WLOG_TRACE: + case WLOG_DEBUG: + return LOG_DEBUG; + case WLOG_INFO: + return LOG_INFO; + case WLOG_WARN: + return LOG_WARNING; + case WLOG_ERROR: + return LOG_ERR; + case WLOG_FATAL: + return LOG_CRIT; + case WLOG_OFF: + default: + return -1; + } +} + +static BOOL WLog_SyslogAppender_Open(wLog* log, wLogAppender* appender) +{ + if (!log || !appender) + return FALSE; + + return TRUE; +} + +static BOOL WLog_SyslogAppender_Close(wLog* log, wLogAppender* appender) +{ + if (!log || !appender) + return FALSE; + + return TRUE; +} + +static BOOL WLog_SyslogAppender_WriteMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + int syslogLevel = 0; + + if (!log || !appender || !message) + return FALSE; + + syslogLevel = getSyslogLevel(message->Level); + if (syslogLevel >= 0) + syslog(syslogLevel, "%s", message->TextString); + + return TRUE; +} + +static BOOL WLog_SyslogAppender_WriteDataMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + int syslogLevel = 0; + + if (!log || !appender || !message) + return FALSE; + + syslogLevel = getSyslogLevel(message->Level); + if (syslogLevel >= 0) + syslog(syslogLevel, "skipped data message of %" PRIuz " bytes", message->Length); + + return TRUE; +} + +static BOOL WLog_SyslogAppender_WriteImageMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + int syslogLevel = 0; + + if (!log || !appender || !message) + return FALSE; + + syslogLevel = getSyslogLevel(message->Level); + if (syslogLevel >= 0) + syslog(syslogLevel, "skipped image (%" PRIuz "x%" PRIuz "x%" PRIuz ")", message->ImageWidth, + message->ImageHeight, message->ImageBpp); + + return TRUE; +} + +static void WLog_SyslogAppender_Free(wLogAppender* appender) +{ + free(appender); +} + +wLogAppender* WLog_SyslogAppender_New(wLog* log) +{ + wLogSyslogAppender* appender = NULL; + + appender = (wLogSyslogAppender*)calloc(1, sizeof(wLogSyslogAppender)); + if (!appender) + return NULL; + + appender->Type = WLOG_APPENDER_SYSLOG; + + appender->Open = WLog_SyslogAppender_Open; + appender->Close = WLog_SyslogAppender_Close; + appender->WriteMessage = WLog_SyslogAppender_WriteMessage; + appender->WriteDataMessage = WLog_SyslogAppender_WriteDataMessage; + appender->WriteImageMessage = WLog_SyslogAppender_WriteImageMessage; + appender->Free = WLog_SyslogAppender_Free; + + return (wLogAppender*)appender; +} diff --git a/winpr/libwinpr/utils/wlog/SyslogAppender.h b/winpr/libwinpr/utils/wlog/SyslogAppender.h new file mode 100644 index 0000000..bbb30d5 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/SyslogAppender.h @@ -0,0 +1,32 @@ +/** + * Copyright © 2015 Thincast Technologies GmbH + * Copyright © 2015 David FORT <contact@hardening-consulting.com> + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WINPR_LIBWINPR_UTILS_WLOG_SYSLOGAPPENDER_H_ +#define WINPR_LIBWINPR_UTILS_WLOG_SYSLOGAPPENDER_H_ + +#include "wlog.h" + +WINPR_ATTR_MALLOC(WLog_Appender_Free, 2) +wLogAppender* WLog_SyslogAppender_New(wLog* log); + +#endif /* WINPR_LIBWINPR_UTILS_WLOG_SYSLOGAPPENDER_H_ */ diff --git a/winpr/libwinpr/utils/wlog/UdpAppender.c b/winpr/libwinpr/utils/wlog/UdpAppender.c new file mode 100644 index 0000000..19501dc --- /dev/null +++ b/winpr/libwinpr/utils/wlog/UdpAppender.c @@ -0,0 +1,222 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright © 2015 Thincast Technologies GmbH + * Copyright © 2015 David FORT <contact@hardening-consulting.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 <winpr/crt.h> +#include <winpr/environment.h> +#include <winpr/winsock.h> + +#include "wlog.h" + +typedef struct +{ + WLOG_APPENDER_COMMON(); + char* host; + struct sockaddr targetAddr; + int targetAddrLen; + SOCKET sock; +} wLogUdpAppender; + +static BOOL WLog_UdpAppender_Open(wLog* log, wLogAppender* appender) +{ + wLogUdpAppender* udpAppender = NULL; + char addressString[256] = { 0 }; + struct addrinfo hints = { 0 }; + struct addrinfo* result = { 0 }; + int status = 0; + size_t addrLen = 0; + char* colonPos = NULL; + + if (!appender) + return FALSE; + + udpAppender = (wLogUdpAppender*)appender; + + if (udpAppender->targetAddrLen) /* already opened */ + return TRUE; + + colonPos = strchr(udpAppender->host, ':'); + + if (!colonPos) + return FALSE; + + addrLen = (colonPos - udpAppender->host); + memcpy(addressString, udpAppender->host, addrLen); + addressString[addrLen] = '\0'; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + status = getaddrinfo(addressString, colonPos + 1, &hints, &result); + + if (status != 0) + return FALSE; + + if (result->ai_addrlen > sizeof(udpAppender->targetAddr)) + { + freeaddrinfo(result); + return FALSE; + } + + memcpy(&udpAppender->targetAddr, result->ai_addr, result->ai_addrlen); + udpAppender->targetAddrLen = (int)result->ai_addrlen; + freeaddrinfo(result); + return TRUE; +} + +static BOOL WLog_UdpAppender_Close(wLog* log, wLogAppender* appender) +{ + if (!log || !appender) + return FALSE; + + return TRUE; +} + +static BOOL WLog_UdpAppender_WriteMessage(wLog* log, wLogAppender* appender, wLogMessage* message) +{ + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + wLogUdpAppender* udpAppender = NULL; + + if (!log || !appender || !message) + return FALSE; + + udpAppender = (wLogUdpAppender*)appender; + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + _sendto(udpAppender->sock, message->PrefixString, (int)strnlen(message->PrefixString, INT_MAX), + 0, &udpAppender->targetAddr, udpAppender->targetAddrLen); + _sendto(udpAppender->sock, message->TextString, (int)strnlen(message->TextString, INT_MAX), 0, + &udpAppender->targetAddr, udpAppender->targetAddrLen); + _sendto(udpAppender->sock, "\n", 1, 0, &udpAppender->targetAddr, udpAppender->targetAddrLen); + return TRUE; +} + +static BOOL WLog_UdpAppender_WriteDataMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + if (!log || !appender || !message) + return FALSE; + + return TRUE; +} + +static BOOL WLog_UdpAppender_WriteImageMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + if (!log || !appender || !message) + return FALSE; + + return TRUE; +} + +static BOOL WLog_UdpAppender_Set(wLogAppender* appender, const char* setting, void* value) +{ + const char target[] = "target"; + wLogUdpAppender* udpAppender = (wLogUdpAppender*)appender; + + /* Just check the value string is not empty */ + if (!value || (strnlen(value, 2) == 0)) + return FALSE; + + if (strncmp(target, setting, sizeof(target))) + return FALSE; + + udpAppender->targetAddrLen = 0; + + if (udpAppender->host) + free(udpAppender->host); + + udpAppender->host = _strdup((const char*)value); + return (udpAppender->host != NULL) && WLog_UdpAppender_Open(NULL, appender); +} + +static void WLog_UdpAppender_Free(wLogAppender* appender) +{ + wLogUdpAppender* udpAppender = NULL; + + if (appender) + { + udpAppender = (wLogUdpAppender*)appender; + + if (udpAppender->sock != INVALID_SOCKET) + { + closesocket(udpAppender->sock); + udpAppender->sock = INVALID_SOCKET; + } + + free(udpAppender->host); + free(udpAppender); + } +} + +wLogAppender* WLog_UdpAppender_New(wLog* log) +{ + wLogUdpAppender* appender = NULL; + DWORD nSize = 0; + LPCSTR name = NULL; + appender = (wLogUdpAppender*)calloc(1, sizeof(wLogUdpAppender)); + + if (!appender) + return NULL; + + appender->Type = WLOG_APPENDER_UDP; + appender->Open = WLog_UdpAppender_Open; + appender->Close = WLog_UdpAppender_Close; + appender->WriteMessage = WLog_UdpAppender_WriteMessage; + appender->WriteDataMessage = WLog_UdpAppender_WriteDataMessage; + appender->WriteImageMessage = WLog_UdpAppender_WriteImageMessage; + appender->Free = WLog_UdpAppender_Free; + appender->Set = WLog_UdpAppender_Set; + appender->sock = _socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if (appender->sock == INVALID_SOCKET) + goto error_sock; + + name = "WLOG_UDP_TARGET"; + nSize = GetEnvironmentVariableA(name, NULL, 0); + + if (nSize) + { + appender->host = (LPSTR)malloc(nSize); + + if (!appender->host) + goto error_open; + + if (GetEnvironmentVariableA(name, appender->host, nSize) != nSize - 1) + goto error_open; + + if (!WLog_UdpAppender_Open(log, (wLogAppender*)appender)) + goto error_open; + } + else + { + appender->host = _strdup("127.0.0.1:20000"); + + if (!appender->host) + goto error_open; + } + + return (wLogAppender*)appender; +error_open: + free(appender->host); + closesocket(appender->sock); +error_sock: + free(appender); + return NULL; +} diff --git a/winpr/libwinpr/utils/wlog/UdpAppender.h b/winpr/libwinpr/utils/wlog/UdpAppender.h new file mode 100644 index 0000000..eda98b9 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/UdpAppender.h @@ -0,0 +1,34 @@ +/** + * Copyright © 2015 Thincast Technologies GmbH + * Copyright © 2015 David FORT <contact@hardening-consulting.com> + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WINPR_LIBWINPR_UTILS_WLOG_UDPAPPENDER_H_ +#define WINPR_LIBWINPR_UTILS_WLOG_UDPAPPENDER_H_ + +#include <winpr/wlog.h> + +#include "wlog.h" + +WINPR_ATTR_MALLOC(WLog_Appender_Free, 2) +wLogAppender* WLog_UdpAppender_New(wLog* log); + +#endif /* WINPR_LIBWINPR_UTILS_WLOG_UDPAPPENDER_H_ */ 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; +} diff --git a/winpr/libwinpr/utils/wlog/wlog.h b/winpr/libwinpr/utils/wlog/wlog.h new file mode 100644 index 0000000..2d4a41e --- /dev/null +++ b/winpr/libwinpr/utils/wlog/wlog.h @@ -0,0 +1,92 @@ +/** + * 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. + */ + +#ifndef WINPR_WLOG_PRIVATE_H +#define WINPR_WLOG_PRIVATE_H + +#include <winpr/wlog.h> + +#define WLOG_MAX_PREFIX_SIZE 512 +#define WLOG_MAX_STRING_SIZE 8192 + +typedef BOOL (*WLOG_APPENDER_OPEN_FN)(wLog* log, wLogAppender* appender); +typedef BOOL (*WLOG_APPENDER_CLOSE_FN)(wLog* log, wLogAppender* appender); +typedef BOOL (*WLOG_APPENDER_WRITE_MESSAGE_FN)(wLog* log, wLogAppender* appender, + wLogMessage* message); +typedef BOOL (*WLOG_APPENDER_WRITE_DATA_MESSAGE_FN)(wLog* log, wLogAppender* appender, + wLogMessage* message); +typedef BOOL (*WLOG_APPENDER_WRITE_IMAGE_MESSAGE_FN)(wLog* log, wLogAppender* appender, + wLogMessage* message); +typedef BOOL (*WLOG_APPENDER_WRITE_PACKET_MESSAGE_FN)(wLog* log, wLogAppender* appender, + wLogMessage* message); +typedef BOOL (*WLOG_APPENDER_SET)(wLogAppender* appender, const char* setting, void* value); +typedef void (*WLOG_APPENDER_FREE)(wLogAppender* appender); + +#define WLOG_APPENDER_COMMON() \ + DWORD Type; \ + BOOL active; \ + wLogLayout* Layout; \ + CRITICAL_SECTION lock; \ + BOOL recursive; \ + void* TextMessageContext; \ + void* DataMessageContext; \ + void* ImageMessageContext; \ + void* PacketMessageContext; \ + WLOG_APPENDER_OPEN_FN Open; \ + WLOG_APPENDER_CLOSE_FN Close; \ + WLOG_APPENDER_WRITE_MESSAGE_FN WriteMessage; \ + WLOG_APPENDER_WRITE_DATA_MESSAGE_FN WriteDataMessage; \ + WLOG_APPENDER_WRITE_IMAGE_MESSAGE_FN WriteImageMessage; \ + WLOG_APPENDER_WRITE_PACKET_MESSAGE_FN WritePacketMessage; \ + WLOG_APPENDER_FREE Free; \ + WLOG_APPENDER_SET Set + +struct s_wLogAppender +{ + WLOG_APPENDER_COMMON(); +}; + +struct s_wLog +{ + LPSTR Name; + LONG FilterLevel; + DWORD Level; + + BOOL IsRoot; + BOOL inherit; + LPSTR* Names; + size_t NameCount; + wLogAppender* Appender; + + wLog* Parent; + wLog** Children; + DWORD ChildrenCount; + DWORD ChildrenSize; + CRITICAL_SECTION lock; + const char* (*custom)(void*); + void* context; +}; + +extern const char* WLOG_LEVELS[7]; +BOOL WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout, wLogMessage* message); + +#include "Layout.h" +#include "Appender.h" + +#endif /* WINPR_WLOG_PRIVATE_H */ |