FreeRDP
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 
45 struct format_option_recurse;
46 
47 struct format_tid_arg
48 {
49  char tid[32];
50 };
51 
52 struct 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 
65 struct 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 
78 WINPR_ATTR_FORMAT_ARG(3, 0)
79 static void WLog_PrintMessagePrefixVA(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 
86 WINPR_ATTR_FORMAT_ARG(3, 4)
87 static 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 
96 static 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 
112 static BOOL log_invalid_fmt(const char* what)
113 {
114  (void)fprintf(stderr, "Invalid format string '%s'\n", what);
115  return FALSE;
116 }
117 
118 static 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 
132 static 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 
141 static BOOL replace_format_string(const char* FormatString, struct format_option_recurse* recurse,
142  char* format, size_t formatlen);
143 
144 static 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 
177 static 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 
234 BOOL 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 
302 wLogLayout* WLog_GetLogLayout(wLog* log)
303 {
304  wLogAppender* appender = NULL;
305  appender = WLog_GetLogAppender(log);
306  return appender->Layout;
307 }
308 
309 BOOL WLog_Layout_SetPrefixFormat(wLog* log, wLogLayout* layout, const char* format)
310 {
311  free(layout->FormatString);
312  layout->FormatString = NULL;
313 
314  if (format)
315  {
316  layout->FormatString = _strdup(format);
317 
318  if (!layout->FormatString)
319  return FALSE;
320  }
321 
322  return TRUE;
323 }
324 
325 wLogLayout* WLog_Layout_New(wLog* log)
326 {
327  LPCSTR prefix = "WLOG_PREFIX";
328  DWORD nSize = 0;
329  char* env = NULL;
330  wLogLayout* layout = NULL;
331  layout = (wLogLayout*)calloc(1, sizeof(wLogLayout));
332 
333  if (!layout)
334  return NULL;
335 
336  nSize = GetEnvironmentVariableA(prefix, NULL, 0);
337 
338  if (nSize)
339  {
340  env = (LPSTR)malloc(nSize);
341 
342  if (!env)
343  {
344  free(layout);
345  return NULL;
346  }
347 
348  if (GetEnvironmentVariableA(prefix, env, nSize) != nSize - 1)
349  {
350  free(env);
351  free(layout);
352  return NULL;
353  }
354  }
355 
356  if (env)
357  layout->FormatString = env;
358  else
359  {
360 #ifdef ANDROID
361  layout->FormatString = _strdup("[pid=%pid:tid=%tid] - [%fn]%{[%ctx]%}: ");
362 #else
363  layout->FormatString =
364  _strdup("[%hr:%mi:%se:%ml] [%pid:%tid] [%lv][%mn] - [%fn]%{[%ctx]%}: ");
365 #endif
366 
367  if (!layout->FormatString)
368  {
369  free(layout);
370  return NULL;
371  }
372  }
373 
374  return layout;
375 }
376 
377 void WLog_Layout_Free(wLog* log, wLogLayout* layout)
378 {
379  if (layout)
380  {
381  if (layout->FormatString)
382  {
383  free(layout->FormatString);
384  layout->FormatString = NULL;
385  }
386 
387  free(layout);
388  }
389 }