20#include <winpr/config.h>
27#include <winpr/assert.h>
28#include <winpr/print.h>
29#include <winpr/sysinfo.h>
30#include <winpr/environment.h>
36#if defined __linux__ && !defined ANDROID
38#include <sys/syscall.h>
42#define MIN(x, y) (((x) < (y)) ? (x) : (y))
45struct format_option_recurse;
58 const char* (*fkt)(
void*);
65 const char* (*ext)(
const struct format_option* opt,
const char* str,
size_t* preplacelen,
67 struct format_option_recurse* recurse;
70struct format_option_recurse
72 struct format_option* options;
77 char buffer[WLOG_MAX_PREFIX_SIZE];
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)
87 (void)vsnprintf(prefix, prefixlen, format, args);
90WINPR_ATTR_FORMAT_ARG(3, 4)
91static
void WLog_PrintMessagePrefix(
char* prefix,
size_t prefixlen,
92 WINPR_FORMAT_ARG const
char* format, ...)
95 va_start(args, format);
96 WLog_PrintMessagePrefixVA(prefix, prefixlen, format, args);
100static const char* get_tid(
void* arg)
102 struct format_tid_arg* targ = arg;
106#if defined __linux__ && !defined ANDROID
108 tid = (size_t)syscall(SYS_gettid);
110 tid = (size_t)GetCurrentThreadId();
112 (void)_snprintf(targ->tid,
sizeof(targ->tid),
"%08" PRIxz, tid);
116static BOOL log_invalid_fmt(
const char* what)
118 (void)fprintf(stderr,
"Invalid format string '%s'\n", what);
122static BOOL check_and_log_format_size(
char* format,
size_t size,
size_t index,
size_t add)
125 if (index + add + 1 >= size)
127 (void)fprintf(stderr,
128 "Format string too long ['%s', max %" PRIuz
", used %" PRIuz
129 ", adding %" PRIuz
"]\n",
130 format, size, index, add);
136static int opt_compare_fn(
const void* a,
const void* b)
138 const char* what = a;
139 const struct format_option* opt = b;
142 return strncmp(what, opt->fmt, opt->fmtlen);
145static BOOL replace_format_string(
const char* FormatString,
struct format_option_recurse* recurse,
146 char* format,
size_t formatlen);
148static const char* skip_if_null(
const struct format_option* opt,
const char* fmt,
149 size_t* preplacelen,
size_t* pskiplen)
153 WINPR_ASSERT(preplacelen);
154 WINPR_ASSERT(pskiplen);
159 const char* str = &fmt[opt->fmtlen];
160 const char* end = strstr(str, opt->replace);
163 *pskiplen = WINPR_ASSERTING_INT_CAST(
size_t, end - fmt) + opt->replacelen;
168 const size_t replacelen = WINPR_ASSERTING_INT_CAST(
size_t, end - str);
170 char buffer[WLOG_MAX_PREFIX_SIZE] = { 0 };
171 memcpy(buffer, str, MIN(replacelen, ARRAYSIZE(buffer) - 1));
173 if (!replace_format_string(buffer, opt->recurse, opt->recurse->buffer,
174 ARRAYSIZE(opt->recurse->buffer)))
177 *preplacelen = strnlen(opt->recurse->buffer, ARRAYSIZE(opt->recurse->buffer));
178 return opt->recurse->buffer;
181static BOOL replace_format_string(
const char* FormatString,
struct format_option_recurse* recurse,
182 char* format,
size_t formatlen)
184 WINPR_ASSERT(FormatString);
185 WINPR_ASSERT(recurse);
189 while (*FormatString)
191 const struct format_option* opt =
192 bsearch(FormatString, recurse->options, recurse->nroptions,
193 sizeof(
struct format_option), opt_compare_fn);
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;
202 replace = opt->ext(opt, FormatString, &replacelen, &fmtlen);
204 arg = opt->fkt(opt->arg.pv);
206 if (replace && (replacelen > 0))
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
214 if (!check_and_log_format_size(format, formatlen, index,
215 WINPR_ASSERTING_INT_CAST(
size_t, rc)))
217 index += WINPR_ASSERTING_INT_CAST(
size_t, rc);
219 FormatString += fmtlen;
224 if (*FormatString ==
'%')
225 return log_invalid_fmt(FormatString);
227 if (!check_and_log_format_size(format, formatlen, index, 1))
229 format[index++] = *FormatString++;
233 if (!check_and_log_format_size(format, formatlen, index, 0))
238BOOL WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout,
const wLogMessage* message,
239 char* prefix,
size_t prefixlen)
241 char format[WLOG_MAX_PREFIX_SIZE] = { 0 };
243 WINPR_ASSERT(layout);
244 WINPR_ASSERT(message);
245 WINPR_ASSERT(prefix);
247 struct format_tid_arg targ = { 0 };
250 GetLocalTime(&localTime);
252 struct format_option_recurse recurse = {
253 .options = NULL, .nroptions = 0, .log = log, .layout = layout, .message = message
256#define ENTRY(x) x, sizeof(x) - 1
257 struct format_option options[] = {
261 { .pv = log->context },
267 { .s = localTime.wDayOfWeek },
270 { ENTRY(
"%dy"), ENTRY(
"%u"), NULL, { .s = localTime.wDay }, NULL, &recurse },
272 { ENTRY(
"%fl"), ENTRY(
"%s"), NULL, { .cpv = message->FileName }, NULL, &recurse },
277 { .cpv = message->FunctionName },
281 { ENTRY(
"%hr"), ENTRY(
"%02u"), NULL, { .s = localTime.wHour }, NULL, &recurse },
285 { .s = message->LineNumber },
291 { .cpv = WLOG_LEVELS[message->Level] },
297 { .s = localTime.wMinute },
304 { .s = localTime.wMilliseconds },
307 { ENTRY(
"%mn"), ENTRY(
"%s"), NULL, { .cpv = log->Name }, NULL, &recurse },
308 { ENTRY(
"%mo"), ENTRY(
"%u"), NULL, { .s = localTime.wMonth }, NULL, &recurse },
312 { .s = GetCurrentProcessId() },
318 { .s = localTime.wSecond },
322 { ENTRY(
"%tid"), ENTRY(
"%s"), get_tid, { .pv = &targ }, NULL, &recurse },
323 { ENTRY(
"%yr"), ENTRY(
"%u"), NULL, { .s = localTime.wYear }, NULL, &recurse },
327 { .pv = log->context },
332 recurse.options = options;
333 recurse.nroptions = ARRAYSIZE(options);
335 if (!replace_format_string(layout->FormatString, &recurse, format, ARRAYSIZE(format)))
338 WINPR_PRAGMA_DIAG_PUSH
339 WINPR_PRAGMA_DIAG_IGNORED_FORMAT_SECURITY
341 WLog_PrintMessagePrefix(prefix, prefixlen, format);
343 WINPR_PRAGMA_DIAG_POP
348wLogLayout* WLog_GetLogLayout(wLog* log)
350 wLogAppender* appender = NULL;
351 appender = WLog_GetLogAppender(log);
352 return appender->Layout;
355BOOL WLog_Layout_SetPrefixFormat(WINPR_ATTR_UNUSED wLog* log, wLogLayout* layout,
358 free(layout->FormatString);
359 layout->FormatString = NULL;
363 layout->FormatString = _strdup(format);
365 if (!layout->FormatString)
372wLogLayout* WLog_Layout_New(WINPR_ATTR_UNUSED wLog* log)
374 LPCSTR prefix =
"WLOG_PREFIX";
377 wLogLayout* layout = NULL;
378 layout = (wLogLayout*)calloc(1,
sizeof(wLogLayout));
383 nSize = GetEnvironmentVariableA(prefix, NULL, 0);
387 env = (LPSTR)malloc(nSize);
395 if (GetEnvironmentVariableA(prefix, env, nSize) != nSize - 1)
404 layout->FormatString = env;
408 layout->FormatString = _strdup(
"[pid=%pid:tid=%tid] - [%fn]%{[%ctx]%}: ");
410 layout->FormatString =
411 _strdup(
"[%hr:%mi:%se:%ml] [%pid:%tid] [%lv][%mn] - [%fn]%{[%ctx]%}: ");
414 if (!layout->FormatString)
424void WLog_Layout_Free(WINPR_ATTR_UNUSED wLog* log, wLogLayout* layout)
428 if (layout->FormatString)
430 free(layout->FormatString);
431 layout->FormatString = NULL;