Coverage Report

Created: 2026-04-12 06:59

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