Coverage Report

Created: 2024-09-08 06:18

/src/FreeRDP/winpr/libwinpr/utils/wlog/Layout.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 <stdio.h>
23
#include <string.h>
24
#include <stdarg.h>
25
26
#include <winpr/crt.h>
27
#include <winpr/assert.h>
28
#include <winpr/print.h>
29
#include <winpr/sysinfo.h>
30
#include <winpr/environment.h>
31
32
#include "wlog.h"
33
34
#include "Layout.h"
35
36
#if defined __linux__ && !defined ANDROID
37
#include <unistd.h>
38
#include <sys/syscall.h>
39
#endif
40
41
#ifndef MIN
42
0
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
43
#endif
44
45
struct format_option_recurse;
46
47
struct format_option
48
{
49
  const char* fmt;
50
  size_t fmtlen;
51
  const char* replace;
52
  size_t replacelen;
53
  const char* (*fkt)(void*);
54
  void* arg;
55
  const char* (*ext)(const struct format_option* opt, const char* str, size_t* preplacelen,
56
                     size_t* pskiplen);
57
  struct format_option_recurse* recurse;
58
};
59
60
struct format_option_recurse
61
{
62
  struct format_option* options;
63
  size_t nroptions;
64
  wLog* log;
65
  wLogLayout* layout;
66
  wLogMessage* message;
67
  char buffer[WLOG_MAX_PREFIX_SIZE];
68
};
69
70
/**
71
 * Log Layout
72
 */
73
WINPR_ATTR_FORMAT_ARG(3, 0)
74
static void WLog_PrintMessagePrefixVA(wLog* log, wLogMessage* message,
75
                                      WINPR_FORMAT_ARG const char* format, va_list args)
76
5.81M
{
77
5.81M
  WINPR_ASSERT(message);
78
5.81M
  (void)vsnprintf(message->PrefixString, WLOG_MAX_PREFIX_SIZE - 1, format, args);
79
5.81M
}
80
81
WINPR_ATTR_FORMAT_ARG(3, 4)
82
static void WLog_PrintMessagePrefix(wLog* log, wLogMessage* message,
83
                                    WINPR_FORMAT_ARG const char* format, ...)
84
5.81M
{
85
5.81M
  va_list args;
86
5.81M
  va_start(args, format);
87
5.81M
  WLog_PrintMessagePrefixVA(log, message, format, args);
88
5.81M
  va_end(args);
89
5.81M
}
90
91
static const char* get_tid(void* arg)
92
5.81M
{
93
5.81M
  char* str = arg;
94
5.81M
  size_t tid = 0;
95
5.81M
#if defined __linux__ && !defined ANDROID
96
  /* On Linux we prefer to see the LWP id */
97
5.81M
  tid = (size_t)syscall(SYS_gettid);
98
#else
99
  tid = (size_t)GetCurrentThreadId();
100
#endif
101
5.81M
  (void)sprintf(str, "%08" PRIxz, tid);
102
5.81M
  return str;
103
5.81M
}
104
105
static BOOL log_invalid_fmt(const char* what)
106
0
{
107
0
  (void)fprintf(stderr, "Invalid format string '%s'\n", what);
108
0
  return FALSE;
109
0
}
110
111
static BOOL check_and_log_format_size(char* format, size_t size, size_t index, size_t add)
112
180M
{
113
  /* format string must be '\0' terminated, so abort at size - 1 */
114
180M
  if (index + add + 1 >= size)
115
0
  {
116
0
    (void)fprintf(stderr,
117
0
                  "Format string too long ['%s', max %" PRIuz ", used %" PRIuz
118
0
                  ", adding %" PRIuz "]\n",
119
0
                  format, size, index, add);
120
0
    return FALSE;
121
0
  }
122
180M
  return TRUE;
123
180M
}
124
125
static int opt_compare_fn(const void* a, const void* b)
126
715M
{
127
715M
  const char* what = a;
128
715M
  const struct format_option* opt = b;
129
715M
  if (!opt)
130
0
    return -1;
131
715M
  return strncmp(what, opt->fmt, opt->fmtlen);
132
715M
}
133
134
static BOOL replace_format_string(const char* FormatString, struct format_option_recurse* recurse,
135
                                  char* format, size_t formatlen);
136
137
static const char* skip_if_null(const struct format_option* opt, const char* fmt,
138
                                size_t* preplacelen, size_t* pskiplen)
139
5.81M
{
140
5.81M
  WINPR_ASSERT(opt);
141
5.81M
  WINPR_ASSERT(fmt);
142
5.81M
  WINPR_ASSERT(preplacelen);
143
5.81M
  WINPR_ASSERT(pskiplen);
144
145
5.81M
  *preplacelen = 0;
146
5.81M
  *pskiplen = 0;
147
148
5.81M
  const char* str = &fmt[opt->fmtlen]; /* Skip first %{ from string */
149
5.81M
  const char* end = strstr(str, opt->replace);
150
5.81M
  if (!end)
151
0
    return NULL;
152
5.81M
  *pskiplen = end - fmt + opt->replacelen;
153
154
5.81M
  if (!opt->arg)
155
5.81M
    return NULL;
156
157
0
  const size_t replacelen = end - str;
158
159
0
  char buffer[WLOG_MAX_PREFIX_SIZE] = { 0 };
160
0
  memcpy(buffer, str, MIN(replacelen, ARRAYSIZE(buffer) - 1));
161
162
0
  if (!replace_format_string(buffer, opt->recurse, opt->recurse->buffer,
163
0
                             ARRAYSIZE(opt->recurse->buffer)))
164
0
    return NULL;
165
166
0
  *preplacelen = strnlen(opt->recurse->buffer, ARRAYSIZE(opt->recurse->buffer));
167
0
  return opt->recurse->buffer;
168
0
}
169
170
static BOOL replace_format_string(const char* FormatString, struct format_option_recurse* recurse,
171
                                  char* format, size_t formatlen)
172
5.81M
{
173
5.81M
  WINPR_ASSERT(FormatString);
174
5.81M
  WINPR_ASSERT(recurse);
175
176
5.81M
  size_t index = 0;
177
178
186M
  while (*FormatString)
179
180M
  {
180
180M
    const struct format_option* opt =
181
180M
        bsearch(FormatString, recurse->options, recurse->nroptions,
182
180M
                sizeof(struct format_option), opt_compare_fn);
183
180M
    if (opt)
184
58.1M
    {
185
58.1M
      size_t replacelen = opt->replacelen;
186
58.1M
      size_t fmtlen = opt->fmtlen;
187
58.1M
      const char* replace = opt->replace;
188
58.1M
      const void* arg = opt->arg;
189
190
58.1M
      if (opt->ext)
191
5.81M
        replace = opt->ext(opt, FormatString, &replacelen, &fmtlen);
192
58.1M
      if (opt->fkt)
193
5.81M
        arg = opt->fkt(opt->arg);
194
195
58.1M
      if (replace && (replacelen > 0))
196
52.3M
      {
197
52.3M
        const int rc = _snprintf(&format[index], formatlen - index, replace, arg);
198
52.3M
        if (rc < 0)
199
0
          return FALSE;
200
52.3M
        if (!check_and_log_format_size(format, formatlen, index, rc))
201
0
          return FALSE;
202
52.3M
        index += rc;
203
52.3M
      }
204
58.1M
      FormatString += fmtlen;
205
58.1M
    }
206
122M
    else
207
122M
    {
208
      /* Unknown format string */
209
122M
      if (*FormatString == '%')
210
0
        return log_invalid_fmt(FormatString);
211
212
122M
      if (!check_and_log_format_size(format, formatlen, index, 1))
213
0
        return FALSE;
214
122M
      format[index++] = *FormatString++;
215
122M
    }
216
180M
  }
217
218
5.81M
  if (!check_and_log_format_size(format, formatlen, index, 0))
219
0
    return FALSE;
220
5.81M
  return TRUE;
221
5.81M
}
222
223
BOOL WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout, wLogMessage* message)
224
5.81M
{
225
5.81M
  char format[WLOG_MAX_PREFIX_SIZE] = { 0 };
226
227
5.81M
  WINPR_ASSERT(layout);
228
5.81M
  WINPR_ASSERT(message);
229
230
5.81M
  char tid[32] = { 0 };
231
5.81M
  SYSTEMTIME localTime = { 0 };
232
5.81M
  GetLocalTime(&localTime);
233
234
5.81M
  struct format_option_recurse recurse = {
235
5.81M
    .options = NULL, .nroptions = 0, .log = log, .layout = layout, .message = message
236
5.81M
  };
237
238
197M
#define ENTRY(x) x, sizeof(x) - 1
239
5.81M
  struct format_option options[] = {
240
5.81M
    { ENTRY("%ctx"), ENTRY("%s"), log->custom, log->context, NULL, &recurse }, /* log context */
241
5.81M
    { ENTRY("%dw"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wDayOfWeek, NULL,
242
5.81M
      &recurse }, /* day of week */
243
5.81M
    { ENTRY("%dy"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wDay, NULL,
244
5.81M
      &recurse }, /* day of year */
245
5.81M
    { ENTRY("%fl"), ENTRY("%s"), NULL, (void*)message->FileName, NULL, &recurse }, /* file */
246
5.81M
    { ENTRY("%fn"), ENTRY("%s"), NULL, (void*)message->FunctionName, NULL,
247
5.81M
      &recurse }, /* function */
248
5.81M
    { ENTRY("%hr"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wHour, NULL,
249
5.81M
      &recurse }, /* hours */
250
5.81M
    { ENTRY("%ln"), ENTRY("%" PRIuz), NULL, (void*)message->LineNumber, NULL,
251
5.81M
      &recurse }, /* line number */
252
5.81M
    { ENTRY("%lv"), ENTRY("%s"), NULL, (void*)WLOG_LEVELS[message->Level], NULL,
253
5.81M
      &recurse }, /* log level */
254
5.81M
    { ENTRY("%mi"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wMinute, NULL,
255
5.81M
      &recurse }, /* minutes */
256
5.81M
    { ENTRY("%ml"), ENTRY("%03u"), NULL, (void*)(size_t)localTime.wMilliseconds, NULL,
257
5.81M
      &recurse },                                                   /* milliseconds */
258
5.81M
    { ENTRY("%mn"), ENTRY("%s"), NULL, log->Name, NULL, &recurse }, /* module name */
259
5.81M
    { ENTRY("%mo"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wMonth, NULL,
260
5.81M
      &recurse }, /* month */
261
5.81M
    { ENTRY("%pid"), ENTRY("%u"), NULL, (void*)(size_t)GetCurrentProcessId(), NULL,
262
5.81M
      &recurse }, /* process id */
263
5.81M
    { ENTRY("%se"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wSecond, NULL,
264
5.81M
      &recurse },                                                 /* seconds */
265
5.81M
    { ENTRY("%tid"), ENTRY("%s"), get_tid, tid, NULL, &recurse }, /* thread id */
266
5.81M
    { ENTRY("%yr"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wYear, NULL,
267
5.81M
      &recurse }, /* year */
268
5.81M
    { ENTRY("%{"), ENTRY("%}"), NULL, log->context, skip_if_null,
269
5.81M
      &recurse }, /* skip if no context */
270
5.81M
  };
271
272
5.81M
  recurse.options = options;
273
5.81M
  recurse.nroptions = ARRAYSIZE(options);
274
275
5.81M
  if (!replace_format_string(layout->FormatString, &recurse, format, ARRAYSIZE(format)))
276
0
    return FALSE;
277
278
5.81M
  WINPR_PRAGMA_DIAG_PUSH
279
5.81M
  WINPR_PRAGMA_DIAG_IGNORED_FORMAT_SECURITY
280
281
5.81M
  WLog_PrintMessagePrefix(log, message, format);
282
283
5.81M
  WINPR_PRAGMA_DIAG_POP
284
285
5.81M
  return TRUE;
286
5.81M
}
287
288
wLogLayout* WLog_GetLogLayout(wLog* log)
289
0
{
290
0
  wLogAppender* appender = NULL;
291
0
  appender = WLog_GetLogAppender(log);
292
0
  return appender->Layout;
293
0
}
294
295
BOOL WLog_Layout_SetPrefixFormat(wLog* log, wLogLayout* layout, const char* format)
296
0
{
297
0
  free(layout->FormatString);
298
0
  layout->FormatString = NULL;
299
300
0
  if (format)
301
0
  {
302
0
    layout->FormatString = _strdup(format);
303
304
0
    if (!layout->FormatString)
305
0
      return FALSE;
306
0
  }
307
308
0
  return TRUE;
309
0
}
310
311
wLogLayout* WLog_Layout_New(wLog* log)
312
1
{
313
1
  LPCSTR prefix = "WLOG_PREFIX";
314
1
  DWORD nSize = 0;
315
1
  char* env = NULL;
316
1
  wLogLayout* layout = NULL;
317
1
  layout = (wLogLayout*)calloc(1, sizeof(wLogLayout));
318
319
1
  if (!layout)
320
0
    return NULL;
321
322
1
  nSize = GetEnvironmentVariableA(prefix, NULL, 0);
323
324
1
  if (nSize)
325
0
  {
326
0
    env = (LPSTR)malloc(nSize);
327
328
0
    if (!env)
329
0
    {
330
0
      free(layout);
331
0
      return NULL;
332
0
    }
333
334
0
    if (GetEnvironmentVariableA(prefix, env, nSize) != nSize - 1)
335
0
    {
336
0
      free(env);
337
0
      free(layout);
338
0
      return NULL;
339
0
    }
340
0
  }
341
342
1
  if (env)
343
0
    layout->FormatString = env;
344
1
  else
345
1
  {
346
#ifdef ANDROID
347
    layout->FormatString = _strdup("[pid=%pid:tid=%tid] - [%fn]%{[%ctx]%}: ");
348
#else
349
1
    layout->FormatString =
350
1
        _strdup("[%hr:%mi:%se:%ml] [%pid:%tid] [%lv][%mn] - [%fn]%{[%ctx]%}: ");
351
1
#endif
352
353
1
    if (!layout->FormatString)
354
0
    {
355
0
      free(layout);
356
0
      return NULL;
357
0
    }
358
1
  }
359
360
1
  return layout;
361
1
}
362
363
void WLog_Layout_Free(wLog* log, wLogLayout* layout)
364
1
{
365
1
  if (layout)
366
1
  {
367
1
    if (layout->FormatString)
368
1
    {
369
1
      free(layout->FormatString);
370
1
      layout->FormatString = NULL;
371
1
    }
372
373
1
    free(layout);
374
1
  }
375
1
}