Coverage Report

Created: 2026-06-15 06:56

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