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
189 while (*FormatString)
190 {
191 const struct format_option* opt =
192 bsearch(FormatString, recurse->options, recurse->nroptions,
193 sizeof(struct format_option), opt_compare_fn);
194 if (opt)
195 {
196 size_t replacelen = opt->replacelen;
197 size_t fmtlen = opt->fmtlen;
198 const char* replace = opt->replace;
199 const void* arg = opt->arg.cpv;
200
201 if (opt->ext)
202 replace = opt->ext(opt, FormatString, &replacelen, &fmtlen);
203 if (opt->fkt)
204 arg = opt->fkt(opt->arg.pv);
205
206 if (replace && (replacelen > 0))
207 {
208 WINPR_PRAGMA_DIAG_PUSH
209 WINPR_PRAGMA_DIAG_IGNORED_FORMAT_NONLITERAL
210 const int rc = _snprintf(&format[index], formatlen - index, replace, arg);
211 WINPR_PRAGMA_DIAG_POP
212 if (rc < 0)
213 return FALSE;
214 if (!check_and_log_format_size(format, formatlen, index,
215 WINPR_ASSERTING_INT_CAST(size_t, rc)))
216 return FALSE;
217 index += WINPR_ASSERTING_INT_CAST(size_t, rc);
218 }
219 FormatString += fmtlen;
220 }
221 else
222 {
223 /* Unknown format string */
224 if (*FormatString == '%')
225 return log_invalid_fmt(FormatString);
226
227 if (!check_and_log_format_size(format, formatlen, index, 1))
228 return FALSE;
229 format[index++] = *FormatString++;
230 }
231 }
232
233 return check_and_log_format_size(format, formatlen, index, 0);
234}
235
236BOOL WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout, const wLogMessage* message,
237 char* prefix, size_t prefixlen)
238{
239 char format[WLOG_MAX_PREFIX_SIZE] = WINPR_C_ARRAY_INIT;
240
241 WINPR_ASSERT(layout);
242 WINPR_ASSERT(message);
243 WINPR_ASSERT(prefix);
244
245 struct format_tid_arg targ = WINPR_C_ARRAY_INIT;
246
247 SYSTEMTIME localTime = WINPR_C_ARRAY_INIT;
248 GetLocalTime(&localTime);
249
250 struct format_option_recurse recurse = {
251 .options = nullptr, .nroptions = 0, .log = log, .layout = layout, .message = message
252 };
253
254#define ENTRY(x) x, sizeof(x) - 1
255 struct format_option options[] = {
256 { ENTRY("%ctx"),
257 ENTRY("%s"),
258 log->custom,
259 { .pv = log->context },
260 nullptr,
261 &recurse }, /* log context */
262 { ENTRY("%dw"),
263 ENTRY("%u"),
264 nullptr,
265 { .s = localTime.wDayOfWeek },
266 nullptr,
267 &recurse }, /* day of week */
268 { ENTRY("%dy"),
269 ENTRY("%u"),
270 nullptr,
271 { .s = localTime.wDay },
272 nullptr,
273 &recurse }, /* day of year
274 */
275 { ENTRY("%fl"),
276 ENTRY("%s"),
277 nullptr,
278 { .cpv = message->FileName },
279 nullptr,
280 &recurse }, /* file
281 */
282 { ENTRY("%fn"),
283 ENTRY("%s"),
284 nullptr,
285 { .cpv = message->FunctionName },
286 nullptr,
287 &recurse }, /* function
288 */
289 { ENTRY("%hr"),
290 ENTRY("%02u"),
291 nullptr,
292 { .s = localTime.wHour },
293 nullptr,
294 &recurse }, /* hours
295 */
296 { ENTRY("%ln"),
297 ENTRY("%" PRIuz),
298 nullptr,
299 { .s = message->LineNumber },
300 nullptr,
301 &recurse }, /* line number */
302 { ENTRY("%lv"),
303 ENTRY("%s"),
304 nullptr,
305 { .cpv = WLOG_LEVELS[message->Level] },
306 nullptr,
307 &recurse }, /* log level */
308 { ENTRY("%mi"),
309 ENTRY("%02u"),
310 nullptr,
311 { .s = localTime.wMinute },
312 nullptr,
313 &recurse }, /* minutes
314 */
315 { ENTRY("%ml"),
316 ENTRY("%03u"),
317 nullptr,
318 { .s = localTime.wMilliseconds },
319 nullptr,
320 &recurse }, /* milliseconds */
321 { ENTRY("%mn"), ENTRY("%s"), nullptr, { .cpv = log->Name }, nullptr, &recurse }, /* module
322 name */
323 { ENTRY("%mo"),
324 ENTRY("%u"),
325 nullptr,
326 { .s = localTime.wMonth },
327 nullptr,
328 &recurse }, /* month
329 */
330 { ENTRY("%pid"),
331 ENTRY("%u"),
332 nullptr,
333 { .s = GetCurrentProcessId() },
334 nullptr,
335 &recurse }, /* process id */
336 { ENTRY("%se"),
337 ENTRY("%02u"),
338 nullptr,
339 { .s = localTime.wSecond },
340 nullptr,
341 &recurse }, /* seconds
342 */
343 { ENTRY("%tid"), ENTRY("%s"), get_tid, { .pv = &targ }, nullptr, &recurse }, /* thread id */
344 { ENTRY("%yr"), ENTRY("%u"), nullptr, { .s = localTime.wYear }, nullptr, &recurse }, /* year
345 */
346 { ENTRY("%{"),
347 ENTRY("%}"),
348 nullptr,
349 { .pv = log->context },
350 skip_if_null,
351 &recurse }, /* skip if no context */
352 };
353
354 recurse.options = options;
355 recurse.nroptions = ARRAYSIZE(options);
356
357 if (!replace_format_string(layout->FormatString, &recurse, format, ARRAYSIZE(format)))
358 return FALSE;
359
360 WINPR_PRAGMA_DIAG_PUSH
361 WINPR_PRAGMA_DIAG_IGNORED_FORMAT_SECURITY
362
363 WLog_PrintMessagePrefix(prefix, prefixlen, format);
364
365 WINPR_PRAGMA_DIAG_POP
366
367 return TRUE;
368}
369
370wLogLayout* WLog_GetLogLayout(wLog* log)
371{
372 wLogAppender* appender = nullptr;
373 appender = WLog_GetLogAppender(log);
374 return appender->Layout;
375}
376
377BOOL WLog_Layout_SetPrefixFormat(WINPR_ATTR_UNUSED wLog* log, wLogLayout* layout,
378 const char* format)
379{
380 free(layout->FormatString);
381 layout->FormatString = nullptr;
382
383 if (format)
384 {
385 layout->FormatString = _strdup(format);
386
387 if (!layout->FormatString)
388 return FALSE;
389 }
390
391 return TRUE;
392}
393
394wLogLayout* WLog_Layout_New(WINPR_ATTR_UNUSED wLog* log)
395{
396 LPCSTR prefix = "WLOG_PREFIX";
397 DWORD nSize = 0;
398 char* env = nullptr;
399 wLogLayout* layout = nullptr;
400 layout = (wLogLayout*)calloc(1, sizeof(wLogLayout));
401
402 if (!layout)
403 return nullptr;
404
405 nSize = GetEnvironmentVariableA(prefix, nullptr, 0);
406
407 if (nSize)
408 {
409 env = (LPSTR)malloc(nSize);
410
411 if (!env)
412 {
413 free(layout);
414 return nullptr;
415 }
416
417 if (GetEnvironmentVariableA(prefix, env, nSize) != nSize - 1)
418 {
419 free(env);
420 free(layout);
421 return nullptr;
422 }
423 }
424
425 if (env)
426 layout->FormatString = env;
427 else
428 {
429#ifdef ANDROID
430 layout->FormatString = _strdup("[pid=%pid:tid=%tid] - [%fn]%{[%ctx]%}: ");
431#else
432 layout->FormatString =
433 _strdup("[%hr:%mi:%se:%ml] [%pid:%tid] [%lv][%mn] - [%fn]%{[%ctx]%}: ");
434#endif
435
436 if (!layout->FormatString)
437 {
438 free(layout);
439 return nullptr;
440 }
441 }
442
443 return layout;
444}
445
446void WLog_Layout_Free(WINPR_ATTR_UNUSED wLog* log, wLogLayout* layout)
447{
448 if (layout)
449 {
450 if (layout->FormatString)
451 {
452 free(layout->FormatString);
453 layout->FormatString = nullptr;
454 }
455
456 free(layout);
457 }
458}