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;
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 NULL;
163 *pskiplen = WINPR_ASSERTING_INT_CAST(size_t, end - fmt) + opt->replacelen;
164
165 if (!opt->arg.cpv)
166 return NULL;
167
168 const size_t replacelen = WINPR_ASSERTING_INT_CAST(size_t, end - str);
169
170 char buffer[WLOG_MAX_PREFIX_SIZE] = { 0 };
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 NULL;
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 if (!check_and_log_format_size(format, formatlen, index, 0))
234 return FALSE;
235 return TRUE;
236}
237
238BOOL WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout, const wLogMessage* message,
239 char* prefix, size_t prefixlen)
240{
241 char format[WLOG_MAX_PREFIX_SIZE] = { 0 };
242
243 WINPR_ASSERT(layout);
244 WINPR_ASSERT(message);
245 WINPR_ASSERT(prefix);
246
247 struct format_tid_arg targ = { 0 };
248
249 SYSTEMTIME localTime = { 0 };
250 GetLocalTime(&localTime);
251
252 struct format_option_recurse recurse = {
253 .options = NULL, .nroptions = 0, .log = log, .layout = layout, .message = message
254 };
255
256#define ENTRY(x) x, sizeof(x) - 1
257 struct format_option options[] = {
258 { ENTRY("%ctx"),
259 ENTRY("%s"),
260 log->custom,
261 { .pv = log->context },
262 NULL,
263 &recurse }, /* log context */
264 { ENTRY("%dw"),
265 ENTRY("%u"),
266 NULL,
267 { .s = localTime.wDayOfWeek },
268 NULL,
269 &recurse }, /* day of week */
270 { ENTRY("%dy"), ENTRY("%u"), NULL, { .s = localTime.wDay }, NULL, &recurse }, /* day of year
271 */
272 { ENTRY("%fl"), ENTRY("%s"), NULL, { .cpv = message->FileName }, NULL, &recurse }, /* file
273 */
274 { ENTRY("%fn"),
275 ENTRY("%s"),
276 NULL,
277 { .cpv = message->FunctionName },
278 NULL,
279 &recurse }, /* function
280 */
281 { ENTRY("%hr"), ENTRY("%02u"), NULL, { .s = localTime.wHour }, NULL, &recurse }, /* hours */
282 { ENTRY("%ln"),
283 ENTRY("%" PRIuz),
284 NULL,
285 { .s = message->LineNumber },
286 NULL,
287 &recurse }, /* line number */
288 { ENTRY("%lv"),
289 ENTRY("%s"),
290 NULL,
291 { .cpv = WLOG_LEVELS[message->Level] },
292 NULL,
293 &recurse }, /* log level */
294 { ENTRY("%mi"),
295 ENTRY("%02u"),
296 NULL,
297 { .s = localTime.wMinute },
298 NULL,
299 &recurse }, /* minutes
300 */
301 { ENTRY("%ml"),
302 ENTRY("%03u"),
303 NULL,
304 { .s = localTime.wMilliseconds },
305 NULL,
306 &recurse }, /* milliseconds */
307 { ENTRY("%mn"), ENTRY("%s"), NULL, { .cpv = log->Name }, NULL, &recurse }, /* module name */
308 { ENTRY("%mo"), ENTRY("%u"), NULL, { .s = localTime.wMonth }, NULL, &recurse }, /* month */
309 { ENTRY("%pid"),
310 ENTRY("%u"),
311 NULL,
312 { .s = GetCurrentProcessId() },
313 NULL,
314 &recurse }, /* process id */
315 { ENTRY("%se"),
316 ENTRY("%02u"),
317 NULL,
318 { .s = localTime.wSecond },
319 NULL,
320 &recurse }, /* seconds
321 */
322 { ENTRY("%tid"), ENTRY("%s"), get_tid, { .pv = &targ }, NULL, &recurse }, /* thread id */
323 { ENTRY("%yr"), ENTRY("%u"), NULL, { .s = localTime.wYear }, NULL, &recurse }, /* year */
324 { ENTRY("%{"),
325 ENTRY("%}"),
326 NULL,
327 { .pv = log->context },
328 skip_if_null,
329 &recurse }, /* skip if no context */
330 };
331
332 recurse.options = options;
333 recurse.nroptions = ARRAYSIZE(options);
334
335 if (!replace_format_string(layout->FormatString, &recurse, format, ARRAYSIZE(format)))
336 return FALSE;
337
338 WINPR_PRAGMA_DIAG_PUSH
339 WINPR_PRAGMA_DIAG_IGNORED_FORMAT_SECURITY
340
341 WLog_PrintMessagePrefix(prefix, prefixlen, format);
342
343 WINPR_PRAGMA_DIAG_POP
344
345 return TRUE;
346}
347
348wLogLayout* WLog_GetLogLayout(wLog* log)
349{
350 wLogAppender* appender = NULL;
351 appender = WLog_GetLogAppender(log);
352 return appender->Layout;
353}
354
355BOOL WLog_Layout_SetPrefixFormat(WINPR_ATTR_UNUSED wLog* log, wLogLayout* layout,
356 const char* format)
357{
358 free(layout->FormatString);
359 layout->FormatString = NULL;
360
361 if (format)
362 {
363 layout->FormatString = _strdup(format);
364
365 if (!layout->FormatString)
366 return FALSE;
367 }
368
369 return TRUE;
370}
371
372wLogLayout* WLog_Layout_New(WINPR_ATTR_UNUSED wLog* log)
373{
374 LPCSTR prefix = "WLOG_PREFIX";
375 DWORD nSize = 0;
376 char* env = NULL;
377 wLogLayout* layout = NULL;
378 layout = (wLogLayout*)calloc(1, sizeof(wLogLayout));
379
380 if (!layout)
381 return NULL;
382
383 nSize = GetEnvironmentVariableA(prefix, NULL, 0);
384
385 if (nSize)
386 {
387 env = (LPSTR)malloc(nSize);
388
389 if (!env)
390 {
391 free(layout);
392 return NULL;
393 }
394
395 if (GetEnvironmentVariableA(prefix, env, nSize) != nSize - 1)
396 {
397 free(env);
398 free(layout);
399 return NULL;
400 }
401 }
402
403 if (env)
404 layout->FormatString = env;
405 else
406 {
407#ifdef ANDROID
408 layout->FormatString = _strdup("[pid=%pid:tid=%tid] - [%fn]%{[%ctx]%}: ");
409#else
410 layout->FormatString =
411 _strdup("[%hr:%mi:%se:%ml] [%pid:%tid] [%lv][%mn] - [%fn]%{[%ctx]%}: ");
412#endif
413
414 if (!layout->FormatString)
415 {
416 free(layout);
417 return NULL;
418 }
419 }
420
421 return layout;
422}
423
424void WLog_Layout_Free(WINPR_ATTR_UNUSED wLog* log, wLogLayout* layout)
425{
426 if (layout)
427 {
428 if (layout->FormatString)
429 {
430 free(layout->FormatString);
431 layout->FormatString = NULL;
432 }
433
434 free(layout);
435 }
436}