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, ...)
94 va_list args = WINPR_C_ARRAY_INIT;
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] = WINPR_C_ARRAY_INIT;
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 return check_and_log_format_size(format, formatlen, index, 0);
236BOOL WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout,
const wLogMessage* message,
237 char* prefix,
size_t prefixlen)
239 char format[WLOG_MAX_PREFIX_SIZE] = WINPR_C_ARRAY_INIT;
241 WINPR_ASSERT(layout);
242 WINPR_ASSERT(message);
243 WINPR_ASSERT(prefix);
245 struct format_tid_arg targ = WINPR_C_ARRAY_INIT;
248 GetLocalTime(&localTime);
250 struct format_option_recurse recurse = {
251 .options =
nullptr, .nroptions = 0, .log = log, .layout = layout, .message = message
254#define ENTRY(x) x, sizeof(x) - 1
255 struct format_option options[] = {
259 { .pv = log->context },
265 { .s = localTime.wDayOfWeek },
271 { .s = localTime.wDay },
278 { .cpv = message->FileName },
285 { .cpv = message->FunctionName },
292 { .s = localTime.wHour },
299 { .s = message->LineNumber },
305 { .cpv = WLOG_LEVELS[message->Level] },
311 { .s = localTime.wMinute },
318 { .s = localTime.wMilliseconds },
321 { ENTRY(
"%mn"), ENTRY(
"%s"),
nullptr, { .cpv = log->Name },
nullptr, &recurse },
326 { .s = localTime.wMonth },
333 { .s = GetCurrentProcessId() },
339 { .s = localTime.wSecond },
343 { ENTRY(
"%tid"), ENTRY(
"%s"), get_tid, { .pv = &targ },
nullptr, &recurse },
344 { ENTRY(
"%yr"), ENTRY(
"%u"),
nullptr, { .s = localTime.wYear },
nullptr, &recurse },
349 { .pv = log->context },
354 recurse.options = options;
355 recurse.nroptions = ARRAYSIZE(options);
357 if (!replace_format_string(layout->FormatString, &recurse, format, ARRAYSIZE(format)))
360 WINPR_PRAGMA_DIAG_PUSH
361 WINPR_PRAGMA_DIAG_IGNORED_FORMAT_SECURITY
363 WLog_PrintMessagePrefix(prefix, prefixlen, format);
365 WINPR_PRAGMA_DIAG_POP
370wLogLayout* WLog_GetLogLayout(wLog* log)
372 wLogAppender* appender =
nullptr;
373 appender = WLog_GetLogAppender(log);
374 return appender->Layout;
377BOOL WLog_Layout_SetPrefixFormat(WINPR_ATTR_UNUSED wLog* log, wLogLayout* layout,
380 free(layout->FormatString);
381 layout->FormatString =
nullptr;
385 layout->FormatString = _strdup(format);
387 if (!layout->FormatString)
394wLogLayout* WLog_Layout_New(WINPR_ATTR_UNUSED wLog* log)
396 LPCSTR prefix =
"WLOG_PREFIX";
399 wLogLayout* layout =
nullptr;
400 layout = (wLogLayout*)calloc(1,
sizeof(wLogLayout));
405 nSize = GetEnvironmentVariableA(prefix,
nullptr, 0);
409 env = (LPSTR)malloc(nSize);
417 if (GetEnvironmentVariableA(prefix, env, nSize) != nSize - 1)
426 layout->FormatString = env;
430 layout->FormatString = _strdup(
"[pid=%pid:tid=%tid] - [%fn]%{[%ctx]%}: ");
432 layout->FormatString =
433 _strdup(
"[%hr:%mi:%se:%ml] [%pid:%tid] [%lv][%mn] - [%fn]%{[%ctx]%}: ");
436 if (!layout->FormatString)
446void WLog_Layout_Free(WINPR_ATTR_UNUSED wLog* log, wLogLayout* layout)
450 if (layout->FormatString)
452 free(layout->FormatString);
453 layout->FormatString =
nullptr;