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