Coverage Report

Created: 2025-08-26 06:33

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