FreeRDP
Loading...
Searching...
No Matches
Layout.c
1
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#define MIN(x, y) (((x) < (y)) ? (x) : (y))
43#endif
44
45struct format_option_recurse;
46
47struct format_tid_arg
48{
49 char tid[32];
50};
51
52struct format_option
53{
54 const char* fmt;
55 size_t fmtlen;
56 const char* replace;
57 size_t replacelen;
58 const char* (*fkt)(void*);
59 union
60 {
61 void* pv;
62 const void* cpv;
63 size_t s;
64 } arg;
65 const char* (*ext)(const struct format_option* opt, const char* str, size_t* preplacelen,
66 size_t* pskiplen);
67 struct format_option_recurse* recurse;
68};
69
70struct format_option_recurse
71{
72 struct format_option* options;
73 size_t nroptions;
74 wLog* log;
75 wLogLayout* layout;
76 const wLogMessage* message;
77 char buffer[WLOG_MAX_PREFIX_SIZE];
78};
79
83WINPR_ATTR_FORMAT_ARG(3, 0)
84static void WLog_PrintMessagePrefixVA(char* prefix, size_t prefixlen,
85 WINPR_FORMAT_ARG const char* format, va_list args)
86{
87 (void)vsnprintf(prefix, prefixlen, format, args);
88}
89
90WINPR_ATTR_FORMAT_ARG(3, 4)
91static void WLog_PrintMessagePrefix(char* prefix, size_t prefixlen,
92 WINPR_FORMAT_ARG const char* format, ...)
93{
94 va_list args = WINPR_C_ARRAY_INIT;
95 va_start(args, format);
96 WLog_PrintMessagePrefixVA(prefix, prefixlen, format, args);
97 va_end(args);
98}
99
100static const char* get_tid(void* arg)
101{
102 struct format_tid_arg* targ = arg;
103 WINPR_ASSERT(targ);
104
105 size_t tid = 0;
106#if defined __linux__ && !defined ANDROID
107 /* On Linux we prefer to see the LWP id */
108 tid = (size_t)syscall(SYS_gettid);
109#else
110 tid = (size_t)GetCurrentThreadId();
111#endif
112 (void)_snprintf(targ->tid, sizeof(targ->tid), "%08" PRIxz, tid);
113 return targ->tid;
114}
115
116static BOOL log_invalid_fmt(const char* what)
117{
118 (void)fprintf(stderr, "Invalid format string '%s'\n", what);
119 return FALSE;
120}
121
122static BOOL check_and_log_format_size(char* format, size_t size, size_t index, size_t add)
123{
124 /* format string must be '\0' terminated, so abort at size - 1 */
125 if (index + add + 1 >= size)
126 {
127 (void)fprintf(stderr,
128 "Format string too long ['%s', max %" PRIuz ", used %" PRIuz
129 ", adding %" PRIuz "]\n",
130 format, size, index, add);
131 return FALSE;
132 }
133 return TRUE;
134}
135
136static int opt_compare_fn(const void* a, const void* b)
137{
138 const char* what = a;
139 const struct format_option* opt = b;
140 if (!opt)
141 return -1;
142 return strncmp(what, opt->fmt, opt->fmtlen);
143}
144
145static BOOL replace_format_string(const char* FormatString, struct format_option_recurse* recurse,
146 char* format, size_t formatlen);
147
148static const char* skip_if_null(const struct format_option* opt, const char* fmt,
149 size_t* preplacelen, size_t* pskiplen)
150{
151 WINPR_ASSERT(opt);
152 WINPR_ASSERT(fmt);
153 WINPR_ASSERT(preplacelen);
154 WINPR_ASSERT(pskiplen);
155
156 *preplacelen = 0;
157 *pskiplen = 0;
158
159 const char* str = &fmt[opt->fmtlen]; /* Skip first %{ from string */
160 const char* end = strstr(str, opt->replace);
161 if (!end)
162 return nullptr;
163 *pskiplen = WINPR_ASSERTING_INT_CAST(size_t, end - fmt) + opt->replacelen;
164
165 if (!opt->arg.cpv)
166 return nullptr;
167
168 const size_t replacelen = WINPR_ASSERTING_INT_CAST(size_t, end - str);
169
170 char buffer[WLOG_MAX_PREFIX_SIZE] = WINPR_C_ARRAY_INIT;
171 memcpy(buffer, str, MIN(replacelen, ARRAYSIZE(buffer) - 1));
172
173 if (!replace_format_string(buffer, opt->recurse, opt->recurse->buffer,
174 ARRAYSIZE(opt->recurse->buffer)))
175 return nullptr;
176
177 *preplacelen = strnlen(opt->recurse->buffer, ARRAYSIZE(opt->recurse->buffer));
178 return opt->recurse->buffer;
179}
180
181static BOOL replace_format_string(const char* FormatString, struct format_option_recurse* recurse,
182 char* format, size_t formatlen)
183{
184 WINPR_ASSERT(FormatString);
185 WINPR_ASSERT(recurse);
186
187 size_t index = 0;
188 const char* prefix = WLog_GetGlobalPrefix();
189 if (prefix)
190 {
191 const int res = _snprintf(format, formatlen, "{%s}", prefix);
192 if (res < 0)
193 return FALSE;
194 index = (size_t)res;
195 }
196
197 while (*FormatString)
198 {
199 const struct format_option* opt =
200 bsearch(FormatString, recurse->options, recurse->nroptions,
201 sizeof(struct format_option), opt_compare_fn);
202 if (opt)
203 {
204 size_t replacelen = opt->replacelen;
205 size_t fmtlen = opt->fmtlen;
206 const char* replace = opt->replace;
207 const void* arg = opt->arg.cpv;
208
209 if (opt->ext)
210 replace = opt->ext(opt, FormatString, &replacelen, &fmtlen);
211 if (opt->fkt)
212 arg = opt->fkt(opt->arg.pv);
213
214 if (replace && (replacelen > 0))
215 {
216 WINPR_PRAGMA_DIAG_PUSH
217 WINPR_PRAGMA_DIAG_IGNORED_FORMAT_NONLITERAL
218 const int rc = _snprintf(&format[index], formatlen - index, replace, arg);
219 WINPR_PRAGMA_DIAG_POP
220 if (rc < 0)
221 return FALSE;
222 if (!check_and_log_format_size(format, formatlen, index,
223 WINPR_ASSERTING_INT_CAST(size_t, rc)))
224 return FALSE;
225 index += WINPR_ASSERTING_INT_CAST(size_t, rc);
226 }
227 FormatString += fmtlen;
228 }
229 else
230 {
231 /* Unknown format string */
232 if (*FormatString == '%')
233 return log_invalid_fmt(FormatString);
234
235 if (!check_and_log_format_size(format, formatlen, index, 1))
236 return FALSE;
237 format[index++] = *FormatString++;
238 }
239 }
240
241 return check_and_log_format_size(format, formatlen, index, 0);
242}
243
244BOOL WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout, const wLogMessage* message,
245 char* prefix, size_t prefixlen)
246{
247 char format[WLOG_MAX_PREFIX_SIZE] = WINPR_C_ARRAY_INIT;
248
249 WINPR_ASSERT(layout);
250 WINPR_ASSERT(message);
251 WINPR_ASSERT(prefix);
252
253 struct format_tid_arg targ = WINPR_C_ARRAY_INIT;
254
255 SYSTEMTIME localTime = WINPR_C_ARRAY_INIT;
256 GetLocalTime(&localTime);
257
258 struct format_option_recurse recurse = {
259 .options = nullptr, .nroptions = 0, .log = log, .layout = layout, .message = message
260 };
261
262#define ENTRY(x) x, sizeof(x) - 1
263 struct format_option options[] = {
264 { ENTRY("%ctx"),
265 ENTRY("%s"),
266 log->custom,
267 { .pv = log->context },
268 nullptr,
269 &recurse }, /* log context */
270 { ENTRY("%dw"),
271 ENTRY("%u"),
272 nullptr,
273 { .s = localTime.wDayOfWeek },
274 nullptr,
275 &recurse }, /* day of week */
276 { ENTRY("%dy"),
277 ENTRY("%u"),
278 nullptr,
279 { .s = localTime.wDay },
280 nullptr,
281 &recurse }, /* day of year
282 */
283 { ENTRY("%fl"),
284 ENTRY("%s"),
285 nullptr,
286 { .cpv = message->FileName },
287 nullptr,
288 &recurse }, /* file
289 */
290 { ENTRY("%fn"),
291 ENTRY("%s"),
292 nullptr,
293 { .cpv = message->FunctionName },
294 nullptr,
295 &recurse }, /* function
296 */
297 { ENTRY("%hr"),
298 ENTRY("%02u"),
299 nullptr,
300 { .s = localTime.wHour },
301 nullptr,
302 &recurse }, /* hours
303 */
304 { ENTRY("%ln"),
305 ENTRY("%" PRIuz),
306 nullptr,
307 { .s = message->LineNumber },
308 nullptr,
309 &recurse }, /* line number */
310 { ENTRY("%lv"),
311 ENTRY("%s"),
312 nullptr,
313 { .cpv = WLOG_LEVELS[message->Level] },
314 nullptr,
315 &recurse }, /* log level */
316 { ENTRY("%mi"),
317 ENTRY("%02u"),
318 nullptr,
319 { .s = localTime.wMinute },
320 nullptr,
321 &recurse }, /* minutes
322 */
323 { ENTRY("%ml"),
324 ENTRY("%03u"),
325 nullptr,
326 { .s = localTime.wMilliseconds },
327 nullptr,
328 &recurse }, /* milliseconds */
329 { ENTRY("%mn"), ENTRY("%s"), nullptr, { .cpv = log->Name }, nullptr, &recurse }, /* module
330 name */
331 { ENTRY("%mo"),
332 ENTRY("%u"),
333 nullptr,
334 { .s = localTime.wMonth },
335 nullptr,
336 &recurse }, /* month
337 */
338 { ENTRY("%pid"),
339 ENTRY("%u"),
340 nullptr,
341 { .s = GetCurrentProcessId() },
342 nullptr,
343 &recurse }, /* process id */
344 { ENTRY("%se"),
345 ENTRY("%02u"),
346 nullptr,
347 { .s = localTime.wSecond },
348 nullptr,
349 &recurse }, /* seconds
350 */
351 { ENTRY("%tid"), ENTRY("%s"), get_tid, { .pv = &targ }, nullptr, &recurse }, /* thread id */
352 { ENTRY("%yr"), ENTRY("%u"), nullptr, { .s = localTime.wYear }, nullptr, &recurse }, /* year
353 */
354 { ENTRY("%{"),
355 ENTRY("%}"),
356 nullptr,
357 { .pv = log->context },
358 skip_if_null,
359 &recurse }, /* skip if no context */
360 };
361
362 recurse.options = options;
363 recurse.nroptions = ARRAYSIZE(options);
364
365 if (!replace_format_string(layout->FormatString, &recurse, format, ARRAYSIZE(format)))
366 return FALSE;
367
368 WINPR_PRAGMA_DIAG_PUSH
369 WINPR_PRAGMA_DIAG_IGNORED_FORMAT_SECURITY
370
371 WLog_PrintMessagePrefix(prefix, prefixlen, format);
372
373 WINPR_PRAGMA_DIAG_POP
374
375 return TRUE;
376}
377
378wLogLayout* WLog_GetLogLayout(wLog* log)
379{
380 wLogAppender* appender = nullptr;
381 appender = WLog_GetLogAppender(log);
382 return appender->Layout;
383}
384
385BOOL WLog_Layout_SetPrefixFormat(WINPR_ATTR_UNUSED wLog* log, wLogLayout* layout,
386 const char* format)
387{
388 free(layout->FormatString);
389 layout->FormatString = nullptr;
390
391 if (format)
392 {
393 layout->FormatString = _strdup(format);
394
395 if (!layout->FormatString)
396 return FALSE;
397 }
398
399 return TRUE;
400}
401
402wLogLayout* WLog_Layout_New(WINPR_ATTR_UNUSED wLog* log)
403{
404 LPCSTR prefix = "WLOG_PREFIX";
405 DWORD nSize = 0;
406 char* env = nullptr;
407 wLogLayout* layout = nullptr;
408 layout = (wLogLayout*)calloc(1, sizeof(wLogLayout));
409
410 if (!layout)
411 return nullptr;
412
413 nSize = GetEnvironmentVariableA(prefix, nullptr, 0);
414
415 if (nSize)
416 {
417 env = (LPSTR)malloc(nSize);
418
419 if (!env)
420 {
421 free(layout);
422 return nullptr;
423 }
424
425 if (GetEnvironmentVariableA(prefix, env, nSize) != nSize - 1)
426 {
427 free(env);
428 free(layout);
429 return nullptr;
430 }
431 }
432
433 if (env)
434 layout->FormatString = env;
435 else
436 {
437#ifdef ANDROID
438 layout->FormatString = _strdup("[pid=%pid:tid=%tid] - [%fn]%{[%ctx]%}: ");
439#else
440 layout->FormatString =
441 _strdup("[%hr:%mi:%se:%ml] [%pid:%tid] [%lv][%mn] - [%fn]%{[%ctx]%}: ");
442#endif
443
444 if (!layout->FormatString)
445 {
446 free(layout);
447 return nullptr;
448 }
449 }
450
451 return layout;
452}
453
454void WLog_Layout_Free(WINPR_ATTR_UNUSED wLog* log, wLogLayout* layout)
455{
456 if (layout)
457 {
458 if (layout->FormatString)
459 {
460 free(layout->FormatString);
461 layout->FormatString = nullptr;
462 }
463
464 free(layout);
465 }
466}