Coverage Report

Created: 2026-02-26 06:50

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