Coverage Report

Created: 2026-03-07 07:02

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