Coverage Report

Created: 2026-05-11 06:55

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
68.0k
#define WLOG_FILTER_NOT_FILTERED (-1)
53
68.0k
#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
2
  for (DWORD index = 0; index < root->ChildrenCount; index++)
89
1
  {
90
1
    child = root->Children[index];
91
1
    WLog_Free(child);
92
1
  }
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
2
{
102
2
  WINPR_ASSERT(log);
103
2
  EnterCriticalSection(&log->lock);
104
2
}
105
106
static void WLog_Unlock(wLog* log)
107
2
{
108
2
  WINPR_ASSERT(log);
109
2
  LeaveCriticalSection(&log->lock);
110
2
}
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
63.9k
{
233
63.9k
  BOOL status = FALSE;
234
63.9k
  wLogAppender* appender = WLog_GetLogAppender(log);
235
236
63.9k
  if (!appender)
237
0
    return FALSE;
238
239
63.9k
  if (!appender->active)
240
1
    if (!WLog_OpenAppender(log))
241
0
      return FALSE;
242
243
63.9k
  EnterCriticalSection(&appender->lock);
244
245
63.9k
  if (appender->WriteMessage)
246
63.9k
  {
247
63.9k
    if (appender->recursive)
248
0
      status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
249
63.9k
    else
250
63.9k
    {
251
63.9k
      appender->recursive = TRUE;
252
63.9k
      status = appender->WriteMessage(log, appender, message);
253
63.9k
      appender->recursive = FALSE;
254
63.9k
    }
255
63.9k
  }
256
257
63.9k
  LeaveCriticalSection(&appender->lock);
258
63.9k
  return status;
259
63.9k
}
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
63.9k
{
355
63.9k
  assert(cmessage);
356
357
63.9k
  char formattedLogMessage[WLOG_MAX_STRING_SIZE] = WINPR_C_ARRAY_INIT;
358
63.9k
  wLogMessage message = *cmessage;
359
63.9k
  message.TextString = formattedLogMessage;
360
361
63.9k
  WINPR_PRAGMA_DIAG_PUSH
362
63.9k
  WINPR_PRAGMA_DIAG_IGNORED_FORMAT_NONLITERAL
363
63.9k
  if (vsnprintf(formattedLogMessage, ARRAYSIZE(formattedLogMessage) - 1, cmessage->FormatString,
364
63.9k
                args) < 0)
365
0
    return FALSE;
366
63.9k
  WINPR_PRAGMA_DIAG_POP
367
368
63.9k
  return WLog_Write(log, &message);
369
63.9k
}
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
63.9k
{
421
63.9k
  wLogMessage message = WINPR_C_ARRAY_INIT;
422
63.9k
  message.Type = WLOG_MESSAGE_TEXT;
423
63.9k
  message.Level = level;
424
63.9k
  message.LineNumber = line;
425
63.9k
  message.FileName = file;
426
63.9k
  message.FunctionName = function;
427
428
63.9k
  message.FormatString = fmt;
429
430
63.9k
  return WLog_PrintTextMessageInternal(log, &message, args);
431
63.9k
}
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
0
{
447
0
  BOOL status = 0;
448
0
  va_list args = WINPR_C_ARRAY_INIT;
449
0
  va_start(args, fmt);
450
0
  status = WLog_PrintTextMessageVA(log, level, line, file, function, fmt, args);
451
0
  va_end(args);
452
0
  return status;
453
0
}
454
455
DWORD WLog_GetLogLevel(wLog* log)
456
68.0k
{
457
68.0k
  if (!log)
458
0
    return WLOG_OFF;
459
460
68.0k
  if (log->FilterLevel <= WLOG_FILTER_NOT_INITIALIZED)
461
0
    log->FilterLevel = WLog_GetFilterLogLevel(log);
462
463
68.0k
  if (log->FilterLevel > WLOG_FILTER_NOT_FILTERED)
464
0
    return (DWORD)log->FilterLevel;
465
68.0k
  else if (log->Level == WLOG_LEVEL_INHERIT)
466
1
    log->Level = WLog_GetLogLevel(log->Parent);
467
468
68.0k
  return log->Level;
469
68.0k
}
470
471
BOOL WLog_IsLevelActive(wLog* _log, DWORD _log_level)
472
68.0k
{
473
68.0k
  DWORD level = 0;
474
475
68.0k
  if (!_log)
476
0
    return FALSE;
477
478
68.0k
  level = WLog_GetLogLevel(_log);
479
480
68.0k
  if (level == WLOG_OFF)
481
0
    return FALSE;
482
483
68.0k
  return _log_level >= level;
484
68.0k
}
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
2
{
768
2
  BOOL match = FALSE;
769
770
2
  if (log->FilterLevel >= 0)
771
0
    return log->FilterLevel;
772
773
2
  log->FilterLevel = WLOG_FILTER_NOT_FILTERED;
774
2
  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
2
  return log->FilterLevel;
810
2
}
811
812
static BOOL WLog_ParseName(wLog* log, LPCSTR name)
813
2
{
814
2
  const char* cp = name;
815
2
  char* p = nullptr;
816
2
  size_t count = 1;
817
2
  LPSTR names = nullptr;
818
819
4
  while ((cp = strchr(cp, '.')) != nullptr)
820
2
  {
821
2
    count++;
822
2
    cp++;
823
2
  }
824
825
2
  names = _strdup(name);
826
827
2
  if (!names)
828
0
    return FALSE;
829
830
2
  log->NameCount = count;
831
2
  log->Names = (LPSTR*)calloc((count + 1UL), sizeof(LPSTR));
832
833
2
  if (!log->Names)
834
0
  {
835
0
    free(names);
836
0
    return FALSE;
837
0
  }
838
839
2
  log->Names[count] = nullptr;
840
2
  count = 0;
841
2
  p = (char*)names;
842
2
  log->Names[count++] = p;
843
844
4
  while ((p = strchr(p, '.')) != nullptr)
845
2
  {
846
2
    if (count < log->NameCount)
847
2
      log->Names[count++] = p + 1;
848
849
2
    *p = '\0';
850
2
    p++;
851
2
  }
852
853
2
  return TRUE;
854
2
}
855
856
wLog* WLog_New(LPCSTR name, wLog* rootLogger)
857
2
{
858
2
  wLog* log = nullptr;
859
2
  char* env = nullptr;
860
2
  DWORD nSize = 0;
861
2
  int iLevel = 0;
862
2
  log = (wLog*)calloc(1, sizeof(wLog));
863
864
2
  if (!log)
865
0
    return nullptr;
866
867
2
  log->Name = _strdup(name);
868
869
2
  if (!log->Name)
870
0
    goto out_fail;
871
872
2
  if (!WLog_ParseName(log, name))
873
0
    goto out_fail;
874
875
2
  log->Parent = rootLogger;
876
2
  log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED;
877
878
2
  if (rootLogger)
879
1
  {
880
1
    log->Level = WLOG_LEVEL_INHERIT;
881
1
    log->inherit = TRUE;
882
1
  }
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
2
  iLevel = WLog_GetFilterLogLevel(log);
915
916
2
  if (iLevel >= 0)
917
0
  {
918
0
    if (!WLog_SetLogLevel(log, (DWORD)iLevel))
919
0
      goto out_fail;
920
0
  }
921
922
2
  if (!InitializeCriticalSectionAndSpinCount(&log->lock, 4000))
923
0
    goto out_fail;
924
925
2
  return log;
926
0
out_fail:
927
0
  WLog_Free(log);
928
0
  return nullptr;
929
2
}
930
931
void WLog_Free(wLog* log)
932
2
{
933
2
  if (log)
934
2
  {
935
2
    if (log->Appender)
936
1
    {
937
1
      WLog_Appender_Free(log, log->Appender);
938
1
      log->Appender = nullptr;
939
1
    }
940
941
2
    free(log->Name);
942
943
    /* The first element in this array is allocated, the rest are indices into this variable */
944
2
    if (log->Names)
945
2
      free(log->Names[0]);
946
2
    free((void*)log->Names);
947
2
    free((void*)log->Children);
948
2
    DeleteCriticalSection(&log->lock);
949
2
    free(log);
950
2
  }
951
2
}
952
953
wLog* WLog_GetRoot(void)
954
1
{
955
1
  if (!InitOnceExecuteOnce(&g_WLogInitialized, WLog_InitializeRoot, nullptr, nullptr))
956
0
    return nullptr;
957
958
1
  return g_RootLog;
959
1
}
960
961
static BOOL WLog_AddChild(wLog* parent, wLog* child)
962
1
{
963
1
  BOOL status = FALSE;
964
965
1
  WLog_Lock(parent);
966
967
1
  if (parent->ChildrenCount >= parent->ChildrenSize)
968
1
  {
969
1
    parent->ChildrenSize = parent->ChildrenCount + 4;
970
971
1
    if (parent->ChildrenSize == 0)
972
0
    {
973
0
      free((void*)parent->Children);
974
0
      parent->Children = nullptr;
975
0
    }
976
1
    else
977
1
    {
978
1
      wLog** tmp =
979
1
          (wLog**)realloc((void*)parent->Children, sizeof(wLog*) * parent->ChildrenSize);
980
981
1
      if (!tmp)
982
0
      {
983
0
        free((void*)parent->Children);
984
0
        parent->Children = nullptr;
985
0
        goto exit;
986
0
      }
987
988
1
      parent->Children = tmp;
989
1
    }
990
1
  }
991
992
1
  if (!parent->Children)
993
0
    goto exit;
994
995
1
  parent->Children[parent->ChildrenCount++] = child;
996
1
  child->Parent = parent;
997
998
1
  WLog_Unlock(parent);
999
1000
1
  status = TRUE;
1001
1
exit:
1002
1
  return status;
1003
1
}
1004
1005
static wLog* WLog_FindChild(wLog* root, LPCSTR name)
1006
1
{
1007
1
  wLog* child = nullptr;
1008
1009
1
  if (!root)
1010
0
    return nullptr;
1011
1012
1
  WLog_Lock(root);
1013
1014
1
  for (DWORD index = 0; index < root->ChildrenCount; index++)
1015
0
  {
1016
0
    wLog* cchild = root->Children[index];
1017
1018
0
    if (strcmp(cchild->Name, name) == 0)
1019
0
    {
1020
0
      child = cchild;
1021
0
      break;
1022
0
    }
1023
0
  }
1024
1025
1
  WLog_Unlock(root);
1026
1027
1
  return child;
1028
1
}
1029
1030
static wLog* WLog_Get_int(wLog* root, LPCSTR name)
1031
1
{
1032
1
  wLog* log = nullptr;
1033
1034
1
  if (!(log = WLog_FindChild(root, name)))
1035
1
  {
1036
1
    if (!root)
1037
0
      return nullptr;
1038
1039
1
    if (!(log = WLog_New(name, root)))
1040
0
      return nullptr;
1041
1042
1
    if (!WLog_AddChild(root, log))
1043
0
    {
1044
0
      WLog_Free(log);
1045
0
      return nullptr;
1046
0
    }
1047
1
  }
1048
1049
1
  return log;
1050
1
}
1051
1052
wLog* WLog_Get(LPCSTR name)
1053
1
{
1054
1
  wLog* root = WLog_GetRoot();
1055
1
  return WLog_Get_int(root, name);
1056
1
}
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
63.9k
{
1112
63.9k
  return g_GlobalPrefix;
1113
63.9k
}
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
}