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 = end - fmt + opt->replacelen;
160 
161  if (!opt->arg)
162  return NULL;
163 
164  const size_t replacelen = 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, rc))
211  return FALSE;
212  index += rc;
213  }
214  FormatString += fmtlen;
215  }
216  else
217  {
218  /* Unknown format string */
219  if (*FormatString == '%')
220  return log_invalid_fmt(FormatString);
221 
222  if (!check_and_log_format_size(format, formatlen, index, 1))
223  return FALSE;
224  format[index++] = *FormatString++;
225  }
226  }
227 
228  if (!check_and_log_format_size(format, formatlen, index, 0))
229  return FALSE;
230  return TRUE;
231 }
232 
233 BOOL WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout, wLogMessage* message)
234 {
235  char format[WLOG_MAX_PREFIX_SIZE] = { 0 };
236 
237  WINPR_ASSERT(layout);
238  WINPR_ASSERT(message);
239 
240  struct format_tid_arg targ = { 0 };
241 
242  SYSTEMTIME localTime = { 0 };
243  GetLocalTime(&localTime);
244 
245  struct format_option_recurse recurse = {
246  .options = NULL, .nroptions = 0, .log = log, .layout = layout, .message = message
247  };
248 
249 #define ENTRY(x) x, sizeof(x) - 1
250  struct format_option options[] = {
251  { ENTRY("%ctx"), ENTRY("%s"), log->custom, log->context, NULL, &recurse }, /* log context */
252  { ENTRY("%dw"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wDayOfWeek, NULL,
253  &recurse }, /* day of week */
254  { ENTRY("%dy"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wDay, NULL,
255  &recurse }, /* day of year */
256  { ENTRY("%fl"), ENTRY("%s"), NULL, WINPR_CAST_CONST_PTR_AWAY(message->FileName, void*),
257  NULL, &recurse }, /* file */
258  { ENTRY("%fn"), ENTRY("%s"), NULL, WINPR_CAST_CONST_PTR_AWAY(message->FunctionName, void*),
259  NULL, &recurse }, /* function */
260  { ENTRY("%hr"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wHour, NULL,
261  &recurse }, /* hours */
262  { ENTRY("%ln"), ENTRY("%" PRIuz), NULL, (void*)message->LineNumber, NULL,
263  &recurse }, /* line number */
264  { ENTRY("%lv"), ENTRY("%s"), NULL,
265  WINPR_CAST_CONST_PTR_AWAY(WLOG_LEVELS[message->Level], void*), NULL,
266  &recurse }, /* log level */
267  { ENTRY("%mi"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wMinute, NULL,
268  &recurse }, /* minutes */
269  { ENTRY("%ml"), ENTRY("%03u"), NULL, (void*)(size_t)localTime.wMilliseconds, NULL,
270  &recurse }, /* milliseconds */
271  { ENTRY("%mn"), ENTRY("%s"), NULL, log->Name, NULL, &recurse }, /* module name */
272  { ENTRY("%mo"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wMonth, NULL,
273  &recurse }, /* month */
274  { ENTRY("%pid"), ENTRY("%u"), NULL, (void*)(size_t)GetCurrentProcessId(), NULL,
275  &recurse }, /* process id */
276  { ENTRY("%se"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wSecond, NULL,
277  &recurse }, /* seconds */
278  { ENTRY("%tid"), ENTRY("%s"), get_tid, &targ, NULL, &recurse }, /* thread id */
279  { ENTRY("%yr"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wYear, NULL,
280  &recurse }, /* year */
281  { ENTRY("%{"), ENTRY("%}"), NULL, log->context, skip_if_null,
282  &recurse }, /* skip if no context */
283  };
284 
285  recurse.options = options;
286  recurse.nroptions = ARRAYSIZE(options);
287 
288  if (!replace_format_string(layout->FormatString, &recurse, format, ARRAYSIZE(format)))
289  return FALSE;
290 
291  WINPR_PRAGMA_DIAG_PUSH
292  WINPR_PRAGMA_DIAG_IGNORED_FORMAT_SECURITY
293 
294  WLog_PrintMessagePrefix(log, message, format);
295 
296  WINPR_PRAGMA_DIAG_POP
297 
298  return TRUE;
299 }
300 
301 wLogLayout* WLog_GetLogLayout(wLog* log)
302 {
303  wLogAppender* appender = NULL;
304  appender = WLog_GetLogAppender(log);
305  return appender->Layout;
306 }
307 
308 BOOL WLog_Layout_SetPrefixFormat(wLog* log, wLogLayout* layout, const char* format)
309 {
310  free(layout->FormatString);
311  layout->FormatString = NULL;
312 
313  if (format)
314  {
315  layout->FormatString = _strdup(format);
316 
317  if (!layout->FormatString)
318  return FALSE;
319  }
320 
321  return TRUE;
322 }
323 
324 wLogLayout* WLog_Layout_New(wLog* log)
325 {
326  LPCSTR prefix = "WLOG_PREFIX";
327  DWORD nSize = 0;
328  char* env = NULL;
329  wLogLayout* layout = NULL;
330  layout = (wLogLayout*)calloc(1, sizeof(wLogLayout));
331 
332  if (!layout)
333  return NULL;
334 
335  nSize = GetEnvironmentVariableA(prefix, NULL, 0);
336 
337  if (nSize)
338  {
339  env = (LPSTR)malloc(nSize);
340 
341  if (!env)
342  {
343  free(layout);
344  return NULL;
345  }
346 
347  if (GetEnvironmentVariableA(prefix, env, nSize) != nSize - 1)
348  {
349  free(env);
350  free(layout);
351  return NULL;
352  }
353  }
354 
355  if (env)
356  layout->FormatString = env;
357  else
358  {
359 #ifdef ANDROID
360  layout->FormatString = _strdup("[pid=%pid:tid=%tid] - [%fn]%{[%ctx]%}: ");
361 #else
362  layout->FormatString =
363  _strdup("[%hr:%mi:%se:%ml] [%pid:%tid] [%lv][%mn] - [%fn]%{[%ctx]%}: ");
364 #endif
365 
366  if (!layout->FormatString)
367  {
368  free(layout);
369  return NULL;
370  }
371  }
372 
373  return layout;
374 }
375 
376 void WLog_Layout_Free(wLog* log, wLogLayout* layout)
377 {
378  if (layout)
379  {
380  if (layout->FormatString)
381  {
382  free(layout->FormatString);
383  layout->FormatString = NULL;
384  }
385 
386  free(layout);
387  }
388 }