1 /*
2  * Copyright (c) 2002-2014 Balabit
3  * Copyright (c) 1998-2014 Balázs Scheidler
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * As an additional exemption you are allowed to compile & link against the
20  * OpenSSL libraries as published by the OpenSSL project. See the file
21  * COPYING for details.
22  *
23  */
24 
25 #include "template/macros.h"
26 #include "template/escaping.h"
27 #include "timeutils/cache.h"
28 #include "timeutils/names.h"
29 #include "timeutils/unixtime.h"
30 #include "timeutils/format.h"
31 #include "timeutils/misc.h"
32 #include "timeutils/conv.h"
33 #include "messages.h"
34 #include "str-format.h"
35 #include "run-id.h"
36 #include "host-id.h"
37 #include "rcptid.h"
38 #include "logmsg/logmsg.h"
39 #include "syslog-names.h"
40 #include "hostname.h"
41 #include "template/templates.h"
42 #include "cfg.h"
43 
44 #include <string.h>
45 
46 LogMacroDef macros[] =
47 {
48   { "FACILITY", M_FACILITY },
49   { "FACILITY_NUM", M_FACILITY_NUM },
50   { "SEVERITY", M_SEVERITY },
51   { "SEVERITY_NUM", M_SEVERITY_NUM },
52 
53   /* these are obsolete aliases of $SEVERITY that we support for compatibility only */
54   { "PRIORITY", M_SEVERITY },         /* deprecated */
55   { "LEVEL", M_SEVERITY },            /* deprecated */
56   { "LEVEL_NUM", M_SEVERITY_NUM },    /* deprecated */
57 
58   { "TAG", M_TAG },
59   { "TAGS", M_TAGS },
60   { "BSDTAG", M_BSDTAG },
61   { "PRI", M_PRI },
62 
63   { "DATE",           M_DATE },
64   { "FULLDATE",       M_FULLDATE },
65   { "ISODATE",        M_ISODATE },
66   { "STAMP",          M_STAMP },
67   { "YEAR",           M_YEAR },
68   { "YEAR_DAY",       M_YEAR_DAY },
69   { "MONTH",          M_MONTH },
70   { "MONTH_WEEK",     M_MONTH_WEEK },
71   { "MONTH_ABBREV",   M_MONTH_ABBREV },
72   { "MONTH_NAME",     M_MONTH_NAME },
73   { "DAY",            M_DAY },
74   { "HOUR",           M_HOUR },
75   { "HOUR12",         M_HOUR12 },
76   { "MIN",            M_MIN },
77   { "SEC",            M_SEC },
78   { "USEC",           M_USEC },
79   { "MSEC",           M_MSEC },
80   { "AMPM",           M_AMPM },
81   { "WEEKDAY",        M_WEEK_DAY_ABBREV }, /* deprecated */
82   { "WEEK_DAY",       M_WEEK_DAY },
83   { "WEEK_DAY_ABBREV", M_WEEK_DAY_ABBREV },
84   { "WEEK_DAY_NAME",  M_WEEK_DAY_NAME },
85   { "WEEK",           M_WEEK },
86   { "ISOWEEK",        M_ISOWEEK },
87   { "TZOFFSET",       M_TZOFFSET },
88   { "TZ",             M_TZ },
89   { "SYSUPTIME",      M_SYSUPTIME },
90   { "UNIXTIME",       M_UNIXTIME },
91 
92   { "R_DATE",           M_RECVD_OFS + M_DATE },
93   { "R_FULLDATE",       M_RECVD_OFS + M_FULLDATE },
94   { "R_ISODATE",        M_RECVD_OFS + M_ISODATE },
95   { "R_STAMP",          M_RECVD_OFS + M_STAMP },
96   { "R_YEAR",           M_RECVD_OFS + M_YEAR },
97   { "R_YEAR_DAY",       M_RECVD_OFS + M_YEAR_DAY },
98   { "R_MONTH",          M_RECVD_OFS + M_MONTH },
99   { "R_MONTH_WEEK",     M_RECVD_OFS + M_MONTH_WEEK },
100   { "R_MONTH_ABBREV",   M_RECVD_OFS + M_MONTH_ABBREV },
101   { "R_MONTH_NAME",     M_RECVD_OFS + M_MONTH_NAME },
102   { "R_DAY",            M_RECVD_OFS + M_DAY },
103   { "R_HOUR",           M_RECVD_OFS + M_HOUR },
104   { "R_HOUR12",         M_RECVD_OFS + M_HOUR12 },
105   { "R_MIN",            M_RECVD_OFS + M_MIN },
106   { "R_SEC",            M_RECVD_OFS + M_SEC },
107   { "R_MSEC",           M_RECVD_OFS + M_MSEC },
108   { "R_USEC",           M_RECVD_OFS + M_USEC },
109   { "R_AMPM",           M_RECVD_OFS + M_AMPM },
110   { "R_WEEKDAY",        M_RECVD_OFS + M_WEEK_DAY_ABBREV }, /* deprecated */
111   { "R_WEEK_DAY",       M_RECVD_OFS + M_WEEK_DAY },
112   { "R_WEEK_DAY_ABBREV", M_RECVD_OFS + M_WEEK_DAY_ABBREV },
113   { "R_WEEK_DAY_NAME",  M_RECVD_OFS + M_WEEK_DAY_NAME },
114   { "R_WEEK",           M_RECVD_OFS + M_WEEK },
115   { "R_ISOWEEK",        M_RECVD_OFS + M_ISOWEEK },
116   { "R_TZOFFSET",       M_RECVD_OFS + M_TZOFFSET },
117   { "R_TZ",             M_RECVD_OFS + M_TZ },
118   { "R_UNIXTIME",       M_RECVD_OFS + M_UNIXTIME },
119 
120   { "S_DATE",           M_STAMP_OFS + M_DATE },
121   { "S_FULLDATE",       M_STAMP_OFS + M_FULLDATE },
122   { "S_ISODATE",        M_STAMP_OFS + M_ISODATE },
123   { "S_STAMP",          M_STAMP_OFS + M_STAMP },
124   { "S_YEAR",           M_STAMP_OFS + M_YEAR },
125   { "S_YEAR_DAY",       M_STAMP_OFS + M_YEAR_DAY },
126   { "S_MONTH",          M_STAMP_OFS + M_MONTH },
127   { "S_MONTH_WEEK",     M_STAMP_OFS + M_MONTH_WEEK },
128   { "S_MONTH_ABBREV",   M_STAMP_OFS + M_MONTH_ABBREV },
129   { "S_MONTH_NAME",     M_STAMP_OFS + M_MONTH_NAME },
130   { "S_DAY",            M_STAMP_OFS + M_DAY },
131   { "S_HOUR",           M_STAMP_OFS + M_HOUR },
132   { "S_HOUR12",         M_STAMP_OFS + M_HOUR12 },
133   { "S_MIN",            M_STAMP_OFS + M_MIN },
134   { "S_SEC",            M_STAMP_OFS + M_SEC },
135   { "S_MSEC",           M_STAMP_OFS + M_MSEC },
136   { "S_USEC",           M_STAMP_OFS + M_USEC },
137   { "S_AMPM",           M_STAMP_OFS + M_AMPM },
138   { "S_WEEKDAY",        M_STAMP_OFS + M_WEEK_DAY_ABBREV }, /* deprecated */
139   { "S_WEEK_DAY",       M_STAMP_OFS + M_WEEK_DAY },
140   { "S_WEEK_DAY_ABBREV", M_STAMP_OFS + M_WEEK_DAY_ABBREV },
141   { "S_WEEK_DAY_NAME",  M_STAMP_OFS + M_WEEK_DAY_NAME },
142   { "S_WEEK",           M_STAMP_OFS + M_WEEK },
143   { "S_ISOWEEK",        M_STAMP_OFS + M_ISOWEEK },
144   { "S_TZOFFSET",       M_STAMP_OFS + M_TZOFFSET },
145   { "S_TZ",             M_STAMP_OFS + M_TZ },
146   { "S_UNIXTIME",       M_STAMP_OFS + M_UNIXTIME },
147 
148   { "C_DATE",           M_CSTAMP_OFS + M_DATE },
149   { "C_FULLDATE",       M_CSTAMP_OFS + M_FULLDATE },
150   { "C_ISODATE",        M_CSTAMP_OFS + M_ISODATE },
151   { "C_STAMP",          M_CSTAMP_OFS + M_STAMP },
152   { "C_YEAR",           M_CSTAMP_OFS + M_YEAR },
153   { "C_YEAR_DAY",       M_CSTAMP_OFS + M_YEAR_DAY },
154   { "C_MONTH",          M_CSTAMP_OFS + M_MONTH },
155   { "C_MONTH_WEEK",     M_CSTAMP_OFS + M_MONTH_WEEK },
156   { "C_MONTH_ABBREV",   M_CSTAMP_OFS + M_MONTH_ABBREV },
157   { "C_MONTH_NAME",     M_CSTAMP_OFS + M_MONTH_NAME },
158   { "C_DAY",            M_CSTAMP_OFS + M_DAY },
159   { "C_HOUR",           M_CSTAMP_OFS + M_HOUR },
160   { "C_HOUR12",         M_CSTAMP_OFS + M_HOUR12 },
161   { "C_MIN",            M_CSTAMP_OFS + M_MIN },
162   { "C_SEC",            M_CSTAMP_OFS + M_SEC },
163   { "C_MSEC",           M_CSTAMP_OFS + M_MSEC },
164   { "C_USEC",           M_CSTAMP_OFS + M_USEC },
165   { "C_AMPM",           M_CSTAMP_OFS + M_AMPM },
166   { "C_WEEKDAY",        M_CSTAMP_OFS + M_WEEK_DAY_ABBREV }, /* deprecated */
167   { "C_WEEK_DAY",       M_CSTAMP_OFS + M_WEEK_DAY },
168   { "C_WEEK_DAY_ABBREV", M_CSTAMP_OFS + M_WEEK_DAY_ABBREV },
169   { "C_WEEK_DAY_NAME",  M_CSTAMP_OFS + M_WEEK_DAY_NAME },
170   { "C_WEEK",           M_CSTAMP_OFS + M_WEEK },
171   { "C_ISOWEEK",        M_CSTAMP_OFS + M_ISOWEEK },
172   { "C_TZOFFSET",       M_CSTAMP_OFS + M_TZOFFSET },
173   { "C_TZ",             M_CSTAMP_OFS + M_TZ },
174   { "C_UNIXTIME",       M_CSTAMP_OFS + M_UNIXTIME },
175 
176   { "P_DATE",           M_PROCESSED_OFS + M_DATE },
177   { "P_FULLDATE",       M_PROCESSED_OFS + M_FULLDATE },
178   { "P_ISODATE",        M_PROCESSED_OFS + M_ISODATE },
179   { "P_STAMP",          M_PROCESSED_OFS + M_STAMP },
180   { "P_YEAR",           M_PROCESSED_OFS + M_YEAR },
181   { "P_YEAR_DAY",       M_PROCESSED_OFS + M_YEAR_DAY },
182   { "P_MONTH",          M_PROCESSED_OFS + M_MONTH },
183   { "P_MONTH_WEEK",     M_PROCESSED_OFS + M_MONTH_WEEK },
184   { "P_MONTH_ABBREV",   M_PROCESSED_OFS + M_MONTH_ABBREV },
185   { "P_MONTH_NAME",     M_PROCESSED_OFS + M_MONTH_NAME },
186   { "P_DAY",            M_PROCESSED_OFS + M_DAY },
187   { "P_HOUR",           M_PROCESSED_OFS + M_HOUR },
188   { "P_HOUR12",         M_PROCESSED_OFS + M_HOUR12 },
189   { "P_MIN",            M_PROCESSED_OFS + M_MIN },
190   { "P_SEC",            M_PROCESSED_OFS + M_SEC },
191   { "P_MSEC",           M_PROCESSED_OFS + M_MSEC },
192   { "P_USEC",           M_PROCESSED_OFS + M_USEC },
193   { "P_AMPM",           M_PROCESSED_OFS + M_AMPM },
194   { "P_WEEKDAY",        M_PROCESSED_OFS + M_WEEK_DAY_ABBREV }, /* deprecated */
195   { "P_WEEK_DAY",       M_PROCESSED_OFS + M_WEEK_DAY },
196   { "P_WEEK_DAY_ABBREV", M_PROCESSED_OFS + M_WEEK_DAY_ABBREV },
197   { "P_WEEK_DAY_NAME",  M_PROCESSED_OFS + M_WEEK_DAY_NAME },
198   { "P_WEEK",           M_PROCESSED_OFS + M_WEEK },
199   { "P_ISOWEEK",        M_PROCESSED_OFS + M_ISOWEEK },
200   { "P_TZOFFSET",       M_PROCESSED_OFS + M_TZOFFSET },
201   { "P_TZ",             M_PROCESSED_OFS + M_TZ },
202   { "P_UNIXTIME",       M_PROCESSED_OFS + M_UNIXTIME },
203 
204   { "SDATA", M_SDATA },
205   { "MSGHDR", M_MSGHDR },
206   { "SOURCEIP", M_SOURCE_IP },
207   { "DESTIP", M_DEST_IP },
208   { "DESTPORT", M_DEST_PORT },
209   { "PROTO", M_PROTOCOL },
210   { "SEQNUM", M_SEQNUM },
211   { "CONTEXT_ID", M_CONTEXT_ID },
212   { "_", M_CONTEXT_ID },
213   { "RCPTID", M_RCPTID },
214   { "RUNID", M_RUNID },
215   { "HOSTID", M_HOSTID },
216   { "UNIQID", M_UNIQID },
217 
218   /* values that have specific behaviour with older syslog-ng config versions */
219   { "MSG", M_MESSAGE },
220   { "MESSAGE", M_MESSAGE },
221   { "HOST", M_HOST },
222 
223   /* message independent macros */
224   { "LOGHOST", M_LOGHOST },
225   { NULL, 0 }
226 };
227 
228 
229 static GTimeVal app_uptime;
230 static GHashTable *macro_hash;
231 static LogTemplateOptions template_options_for_macro_expand;
232 
233 static void
234 _result_append_value(GString *result, const LogMessage *lm, NVHandle handle, gboolean escape)
235 {
236   const gchar *str;
237   gssize len = 0;
238 
239   str = log_msg_get_value(lm, handle, &len);
240   result_append(result, str, len, escape);
241 }
242 
243 static gboolean
244 _is_message_source_an_ip_address(const LogMessage *msg)
245 {
246   if (!msg->saddr)
247     return FALSE;
248   if (g_sockaddr_inet_check(msg->saddr))
249     return TRUE;
250 #if SYSLOG_NG_ENABLE_IPV6
251   if (g_sockaddr_inet6_check(msg->saddr))
252     return TRUE;
253 #endif
254   return FALSE;
255 }
256 
257 static gboolean
258 _is_message_dest_an_ip_address(const LogMessage *msg)
259 {
260   if (!msg->daddr)
261     return FALSE;
262   if (g_sockaddr_inet_check(msg->daddr))
263     return TRUE;
264 #if SYSLOG_NG_ENABLE_IPV6
265   if (g_sockaddr_inet6_check(msg->daddr))
266     return TRUE;
267 #endif
268   return FALSE;
269 }
270 
271 static void
272 log_macro_expand_date_time(GString *result, gint id, gboolean escape,
273                            LogTemplateEvalOptions *options, const LogMessage *msg)
274 {
275   /* year, month, day */
276   const UnixTime *stamp;
277   UnixTime sstamp;
278   guint tmp_hour;
279 
280   if (id >= M_TIME_FIRST && id <= M_TIME_LAST)
281     {
282       stamp = &msg->timestamps[LM_TS_STAMP];
283     }
284   else if (id >= M_TIME_FIRST + M_RECVD_OFS && id <= M_TIME_LAST + M_RECVD_OFS)
285     {
286       id -= M_RECVD_OFS;
287       stamp = &msg->timestamps[LM_TS_RECVD];
288     }
289   else if (id >= M_TIME_FIRST + M_STAMP_OFS && id <= M_TIME_LAST + M_STAMP_OFS)
290     {
291       id -= M_STAMP_OFS;
292       stamp = &msg->timestamps[LM_TS_STAMP];
293     }
294   else if (id >= M_TIME_FIRST + M_CSTAMP_OFS && id <= M_TIME_LAST + M_CSTAMP_OFS)
295     {
296       id -= M_CSTAMP_OFS;
297       unix_time_set_now(&sstamp);
298       stamp = &sstamp;
299     }
300   else if (id >= M_TIME_FIRST + M_PROCESSED_OFS && id <= M_TIME_LAST + M_PROCESSED_OFS)
301     {
302       id -= M_PROCESSED_OFS;
303       stamp = &msg->timestamps[LM_TS_PROCESSED];
304 
305       if (!unix_time_is_set(stamp))
306         {
307           unix_time_set_now(&sstamp);
308           stamp = &sstamp;
309         }
310     }
311   else
312     {
313       g_assert_not_reached();
314       return;
315     }
316 
317   /* try to use the following zone values in order:
318    *   destination specific timezone, if one is specified
319    *   message specific timezone, if one is specified
320    *   local timezone
321    */
322   WallClockTime wct;
323 
324   convert_unix_time_to_wall_clock_time_with_tz_override(stamp, &wct,
325                                                         time_zone_info_get_offset(options->opts->time_zone_info[options->tz], stamp->ut_sec));
326   switch (id)
327     {
328     case M_WEEK_DAY_ABBREV:
329       g_string_append_len(result, weekday_names_abbrev[wct.wct_wday], WEEKDAY_NAME_ABBREV_LEN);
330       break;
331     case M_WEEK_DAY_NAME:
332       g_string_append(result, weekday_names[wct.wct_wday]);
333       break;
334     case M_WEEK_DAY:
335       format_uint32_padded(result, 0, 0, 10, wct.wct_wday + 1);
336       break;
337     case M_WEEK:
338       format_uint32_padded(result, 2, '0', 10, (wct.wct_yday - (wct.wct_wday - 1 + 7) % 7 + 7) / 7);
339       break;
340     case M_ISOWEEK:
341       format_uint32_padded(result, 2, '0', 10, wall_clock_time_iso_week_number(&wct));
342       break;
343     case M_YEAR:
344       format_uint32_padded(result, 4, '0', 10, wct.wct_year + 1900);
345       break;
346     case M_YEAR_DAY:
347       format_uint32_padded(result, 3, '0', 10, wct.wct_yday + 1);
348       break;
349     case M_MONTH:
350       format_uint32_padded(result, 2, '0', 10, wct.wct_mon + 1);
351       break;
352     case M_MONTH_WEEK:
353       format_uint32_padded(result, 0, 0, 10, ((wct.wct_mday / 7) +
354                                               ((wct.wct_wday > 0) &&
355                                                ((wct.wct_mday % 7) >= wct.wct_wday))));
356       break;
357     case M_MONTH_ABBREV:
358       g_string_append_len(result, month_names_abbrev[wct.wct_mon], MONTH_NAME_ABBREV_LEN);
359       break;
360     case M_MONTH_NAME:
361       g_string_append(result, month_names[wct.wct_mon]);
362       break;
363     case M_DAY:
364       format_uint32_padded(result, 2, '0', 10, wct.wct_mday);
365       break;
366     case M_HOUR:
367       format_uint32_padded(result, 2, '0', 10, wct.wct_hour);
368       break;
369     case M_HOUR12:
370       if (wct.wct_hour < 12)
371         tmp_hour = wct.wct_hour;
372       else
373         tmp_hour = wct.wct_hour - 12;
374 
375       if (tmp_hour == 0)
376         tmp_hour = 12;
377       format_uint32_padded(result, 2, '0', 10, tmp_hour);
378       break;
379     case M_MIN:
380       format_uint32_padded(result, 2, '0', 10, wct.wct_min);
381       break;
382     case M_SEC:
383       format_uint32_padded(result, 2, '0', 10, wct.wct_sec);
384       break;
385     case M_MSEC:
386       format_uint32_padded(result, 3, '0', 10, stamp->ut_usec/1000);
387       break;
388     case M_USEC:
389       format_uint32_padded(result, 6, '0', 10, stamp->ut_usec);
390       break;
391     case M_AMPM:
392       g_string_append(result, wct.wct_hour < 12 ? "AM" : "PM");
393       break;
394     case M_DATE:
395       append_format_wall_clock_time(&wct, result, TS_FMT_BSD, options->opts->frac_digits);
396       break;
397     case M_STAMP:
398       if (options->opts->ts_format == TS_FMT_UNIX)
399         append_format_unix_time(stamp, result, TS_FMT_UNIX, wct.wct_gmtoff, options->opts->frac_digits);
400       else
401         append_format_wall_clock_time(&wct, result, options->opts->ts_format, options->opts->frac_digits);
402       break;
403     case M_ISODATE:
404       append_format_wall_clock_time(&wct, result, TS_FMT_ISO, options->opts->frac_digits);
405       break;
406     case M_FULLDATE:
407       append_format_wall_clock_time(&wct, result, TS_FMT_FULL, options->opts->frac_digits);
408       break;
409     case M_UNIXTIME:
410       append_format_unix_time(stamp, result, TS_FMT_UNIX, wct.wct_gmtoff, options->opts->frac_digits);
411       break;
412     case M_TZ:
413     case M_TZOFFSET:
414       append_format_zone_info(result, wct.wct_gmtoff);
415       break;
416     default:
417       g_assert_not_reached();
418       break;
419     }
420 }
421 
422 gboolean
423 log_macro_expand(GString *result, gint id, gboolean escape, LogTemplateEvalOptions *options, const LogMessage *msg)
424 {
425   switch (id)
426     {
427     case M_FACILITY:
428     {
429       /* facility */
430       const char *n;
431 
432       n = syslog_name_lookup_facility_by_value(msg->pri & LOG_FACMASK);
433       if (n)
434         {
435           g_string_append(result, n);
436         }
437       else
438         {
439           format_uint32_padded(result, 0, 0, 16, (msg->pri & LOG_FACMASK) >> 3);
440         }
441       break;
442     }
443     case M_FACILITY_NUM:
444     {
445       format_uint32_padded(result, 0, 0, 10, (msg->pri & LOG_FACMASK) >> 3);
446       break;
447     }
448     case M_SEVERITY:
449     {
450       /* level */
451       const char *n;
452 
453       n = syslog_name_lookup_severity_by_value(msg->pri & LOG_PRIMASK);
454       if (n)
455         {
456           g_string_append(result, n);
457         }
458       else
459         {
460           format_uint32_padded(result, 0, 0, 10, msg->pri & LOG_PRIMASK);
461         }
462 
463       break;
464     }
465     case M_SEVERITY_NUM:
466     {
467       format_uint32_padded(result, 0, 0, 10, msg->pri & LOG_PRIMASK);
468       break;
469     }
470     case M_TAG:
471     {
472       format_uint32_padded(result, 2, '0', 16, msg->pri);
473       break;
474     }
475     case M_TAGS:
476     {
477       log_msg_print_tags(msg, result);
478       break;
479     }
480     case M_BSDTAG:
481     {
482       format_uint32_padded(result, 0, 0, 10, (msg->pri & LOG_PRIMASK));
483       g_string_append_c(result, (((msg->pri & LOG_FACMASK) >> 3) + 'A'));
484       break;
485     }
486     case M_PRI:
487     {
488       format_uint32_padded(result, 0, 0, 10, msg->pri);
489       break;
490     }
491     case M_HOST:
492     {
493       if (msg->flags & LF_CHAINED_HOSTNAME)
494         {
495           /* host */
496           const gchar *p1, *p2;
497           int remaining, length;
498           gssize host_len;
499           const gchar *host = log_msg_get_value(msg, LM_V_HOST, &host_len);
500 
501           p1 = memchr(host, '@', host_len);
502 
503           if (p1)
504             p1++;
505           else
506             p1 = host;
507           remaining = host_len - (p1 - host);
508           p2 = memchr(p1, '/', remaining);
509           length = p2 ? p2 - p1
510                    : host_len - (p1 - host);
511 
512           result_append(result, p1, length, escape);
513         }
514       else
515         {
516           _result_append_value(result, msg, LM_V_HOST, escape);
517         }
518       break;
519     }
520     case M_SDATA:
521     {
522       if (escape)
523         {
524           GString *sdstr = g_string_sized_new(0);
525 
526           log_msg_append_format_sdata(msg, sdstr, options->seq_num);
527           result_append(result, sdstr->str, sdstr->len, TRUE);
528           g_string_free(sdstr, TRUE);
529         }
530       else
531         {
532           log_msg_append_format_sdata(msg, result, options->seq_num);
533         }
534       break;
535     }
536     case M_MSGHDR:
537     {
538       gssize len;
539       const gchar *p;
540 
541       p = log_msg_get_value(msg, LM_V_LEGACY_MSGHDR, &len);
542       if (len > 0)
543         result_append(result, p, len, escape);
544       else
545         {
546           /* message, complete with program name and pid */
547           len = result->len;
548           _result_append_value(result, msg, LM_V_PROGRAM, escape);
549           if (len != result->len)
550             {
551               const gchar *pid = log_msg_get_value(msg, LM_V_PID, &len);
552               if (len > 0)
553                 {
554                   result_append(result, "[", 1, FALSE);
555                   result_append(result, pid, len, escape);
556                   result_append(result, "]", 1, FALSE);
557                 }
558               result_append(result, ": ", 2, FALSE);
559             }
560         }
561       break;
562     }
563     case M_MESSAGE:
564     {
565       _result_append_value(result, msg, LM_V_MESSAGE, escape);
566       break;
567     }
568     case M_SOURCE_IP:
569     {
570       gchar *ip;
571       gchar buf[MAX_SOCKADDR_STRING];
572 
573       if (_is_message_source_an_ip_address(msg))
574         {
575           g_sockaddr_format(msg->saddr, buf, sizeof(buf), GSA_ADDRESS_ONLY);
576           ip = buf;
577         }
578       else
579         {
580           ip = "127.0.0.1";
581         }
582       result_append(result, ip, strlen(ip), escape);
583       break;
584     }
585     case M_DEST_IP:
586     {
587       gchar *ip;
588       gchar buf[MAX_SOCKADDR_STRING];
589 
590       if (_is_message_dest_an_ip_address(msg))
591         {
592           g_sockaddr_format(msg->daddr, buf, sizeof(buf), GSA_ADDRESS_ONLY);
593           ip = buf;
594         }
595       else
596         {
597           ip = "127.0.0.1";
598         }
599       result_append(result, ip, strlen(ip), escape);
600       break;
601     }
602     case M_DEST_PORT:
603     {
604       gint port;
605 
606       if (_is_message_dest_an_ip_address(msg))
607         {
608           port = g_sockaddr_get_port(msg->daddr);
609         }
610       else
611         {
612           port = 0;
613         }
614       format_uint32_padded(result, 0, 0, 10, port);
615       break;
616     }
617     case M_PROTOCOL:
618     {
619       format_uint32_padded(result, 0, 0, 10, msg->proto);
620       break;
621     }
622     case M_SEQNUM:
623     {
624       if (options->seq_num)
625         {
626           format_uint32_padded(result, 0, 0, 10, options->seq_num);
627         }
628       break;
629     }
630     case M_CONTEXT_ID:
631     {
632       if (options->context_id)
633         {
634           result_append(result, options->context_id, strlen(options->context_id), escape);
635         }
636       break;
637     }
638 
639     case M_RCPTID:
640     {
641       rcptid_append_formatted_id(result, msg->rcptid);
642       break;
643     }
644 
645     case M_RUNID:
646     {
647       run_id_append_formatted_id(result);
648       break;
649     }
650 
651     case M_HOSTID:
652     {
653       host_id_append_formatted_id(result, msg->host_id);
654       break;
655     }
656 
657     case M_UNIQID:
658     {
659       if (msg->rcptid)
660         {
661           host_id_append_formatted_id(result, msg->host_id);
662           g_string_append(result, "@");
663           format_uint64_padded(result, 16, '0', 16, msg->rcptid);
664           break;
665         }
666       break;
667     }
668 
669     case M_LOGHOST:
670     {
671       const gchar *hname = options->opts->use_fqdn
672                            ? get_local_hostname_fqdn()
673                            : get_local_hostname_short();
674 
675       result_append(result, hname, -1, escape);
676       break;
677     }
678     case M_SYSUPTIME:
679     {
680       GTimeVal ct;
681 
682       g_get_current_time(&ct);
683       format_uint64_padded(result, 0, 0, 10, g_time_val_diff(&ct, &app_uptime) / 1000 / 10);
684       break;
685     }
686 
687     default:
688     {
689       log_macro_expand_date_time(result, id, escape, options, msg);
690       break;
691     }
692 
693     }
694   return TRUE;
695 }
696 
697 gboolean
698 log_macro_expand_simple(GString *result, gint id, const LogMessage *msg)
699 {
700   LogTemplateEvalOptions options = {&template_options_for_macro_expand, LTZ_LOCAL, 0, NULL};
701   return log_macro_expand(result, id, FALSE, &options, msg);
702 }
703 
704 guint
705 log_macro_lookup(const gchar *macro, gint len)
706 {
707   gchar buf[256];
708   gint macro_id;
709 
710   g_assert(macro_hash);
711   g_strlcpy(buf, macro, MIN(sizeof(buf), len+1));
712   macro_id = GPOINTER_TO_INT(g_hash_table_lookup(macro_hash, buf));
713   return macro_id;
714 }
715 
716 void
717 log_macros_global_init(void)
718 {
719   gint i;
720 
721   /* init the uptime (SYSUPTIME macro) */
722   g_get_current_time(&app_uptime);
723   log_template_options_defaults(&template_options_for_macro_expand);
724 
725   macro_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
726   for (i = 0; macros[i].name; i++)
727     {
728       g_hash_table_insert(macro_hash, g_strdup(macros[i].name),
729                           GINT_TO_POINTER(macros[i].id));
730     }
731   return;
732 }
733 
734 void
735 log_macros_global_deinit(void)
736 {
737   g_hash_table_destroy(macro_hash);
738   macro_hash = NULL;
739 }
740