~ubuntu-branches/ubuntu/wily/syslog-ng/wily-proposed

« back to all changes in this revision

Viewing changes to lib/template/templates.c

  • Committer: Package Import Robot
  • Author(s): Gergely Nagy, Gergely Nagy
  • Date: 2013-11-04 15:27:37 UTC
  • mfrom: (1.3.12)
  • Revision ID: package-import@ubuntu.com-20131104152737-mqh6eqtna2xk97jq
Tags: 3.5.1-1
[ Gergely Nagy <algernon@madhouse-project.org> ]
* New upstream release.
  + Support auto-loading modules (Closes: #650814)
  + The SMTP module is available in syslog-ng-mod-smtp (Closes: #722746)
  + New modules: amqp, geoip, stomp, redis and smtp.
  + Multi-line input support (indented multiline and regexp-based)
  + Template type hinting for the MongoDB destination and $(format-json)
  + Support for unit suffixes in the configuration file
  + New filters, template functions and other miscellaneous changes
* New (team) maintainer, Laszlo Boszormenyi, Attila Szalay and myself
  added to Uploaders.
* Ship /var/lib/syslog-ng in the syslog-ng-core package, instead of
  creating it in the init script. Thanks Michael Biebl
  <biebl@debian.org> for the report & assistance. (Closes: #699942, #719910)
* Use dh-systemd for proper systemd-related maintainer scripts. Based on
  a patch by Michael Biebl <biebl@debian.org>. (Closes: #713982,
  #690067)
* Do not wait for syslog-ng to settle down during installation / update.
  This also fixes installing via debootstrap and a fake
  start-stop-daemon. (Closes: #714254)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2002-2013 BalaBit IT Ltd, Budapest, Hungary
 
3
 * Copyright (c) 1998-2013 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/templates.h"
 
26
#include "messages.h"
 
27
#include "logmsg.h"
 
28
#include "syslog-names.h"
 
29
#include "messages.h"
 
30
#include "misc.h"
 
31
#include "filter/filter-expr.h"
 
32
#include "gsocket.h"
 
33
#include "plugin.h"
 
34
#include "plugin-types.h"
 
35
#include "str-format.h"
 
36
 
 
37
#include <time.h>
 
38
#include <string.h>
 
39
 
 
40
/* macro IDs */
 
41
enum
 
42
{
 
43
  M_NONE,
 
44
 
 
45
  M_FACILITY,
 
46
  M_FACILITY_NUM,
 
47
  M_LEVEL,
 
48
  M_LEVEL_NUM,
 
49
  M_TAG,
 
50
  M_TAGS,
 
51
  M_BSDTAG,
 
52
  M_PRI,
 
53
 
 
54
  M_HOST,
 
55
  M_SDATA,
 
56
 
 
57
  M_MSGHDR,
 
58
  M_MESSAGE,
 
59
  M_SOURCE_IP,
 
60
  M_SEQNUM,
 
61
  M_CONTEXT_ID,
 
62
 
 
63
  M_LOGHOST,
 
64
  M_SYSUPTIME,
 
65
 
 
66
  /* only touch this section if you want to add three macros, one w/o
 
67
   * prefix, and a R_ and S_ prefixed macro that relates one of the
 
68
   * timestamps of the log message. */
 
69
 
 
70
  M_DATE,
 
71
  M_FULLDATE,
 
72
  M_ISODATE,
 
73
  M_STAMP,
 
74
  M_YEAR,
 
75
  M_YEAR_DAY,
 
76
  M_MONTH,
 
77
  M_MONTH_WEEK,
 
78
  M_MONTH_ABBREV,
 
79
  M_MONTH_NAME,
 
80
  M_DAY,
 
81
  M_HOUR,
 
82
  M_HOUR12,
 
83
  M_MIN,
 
84
  M_SEC,
 
85
  M_USEC,
 
86
  M_MSEC,
 
87
  M_AMPM,
 
88
  M_WEEK_DAY,
 
89
  M_WEEK_DAY_ABBREV,
 
90
  M_WEEK_DAY_NAME,
 
91
  M_WEEK,
 
92
  M_TZOFFSET,
 
93
  M_TZ,
 
94
  M_UNIXTIME,
 
95
  M_TIME_FIRST = M_DATE,
 
96
  M_TIME_LAST = M_UNIXTIME,
 
97
  M_TIME_MACROS_MAX = M_UNIXTIME - M_DATE + 1,
 
98
 
 
99
  M_RECVD_OFS = M_TIME_MACROS_MAX,
 
100
  M_STAMP_OFS = 2 * M_TIME_MACROS_MAX,
 
101
  M_CSTAMP_OFS = 3 * M_TIME_MACROS_MAX,
 
102
};
 
103
 
 
104
#define M_TIME_MACROS 15
 
105
 
 
106
LogMacroDef macros[] =
 
107
{
 
108
        { "FACILITY", M_FACILITY },
 
109
        { "FACILITY_NUM", M_FACILITY_NUM },
 
110
        { "PRIORITY", M_LEVEL },
 
111
        { "LEVEL", M_LEVEL },
 
112
        { "LEVEL_NUM", M_LEVEL_NUM },
 
113
        { "TAG", M_TAG },
 
114
        { "TAGS", M_TAGS },
 
115
        { "BSDTAG", M_BSDTAG },
 
116
        { "PRI", M_PRI },
 
117
 
 
118
        { "DATE",           M_DATE },
 
119
        { "FULLDATE",       M_FULLDATE },
 
120
        { "ISODATE",        M_ISODATE },
 
121
        { "STAMP",          M_STAMP },
 
122
        { "YEAR",           M_YEAR },
 
123
        { "YEAR_DAY",       M_YEAR_DAY },
 
124
        { "MONTH",          M_MONTH },
 
125
        { "MONTH_WEEK",     M_MONTH_WEEK },
 
126
        { "MONTH_ABBREV",   M_MONTH_ABBREV },
 
127
        { "MONTH_NAME",     M_MONTH_NAME },
 
128
        { "DAY",            M_DAY },
 
129
        { "HOUR",           M_HOUR },
 
130
        { "HOUR12",         M_HOUR12 },
 
131
        { "MIN",            M_MIN },
 
132
        { "SEC",            M_SEC },
 
133
        { "USEC",           M_USEC },
 
134
        { "MSEC",           M_MSEC },
 
135
        { "AMPM",           M_AMPM },
 
136
        { "WEEKDAY",        M_WEEK_DAY_ABBREV }, /* deprecated */
 
137
        { "WEEK_DAY",       M_WEEK_DAY },
 
138
        { "WEEK_DAY_ABBREV",M_WEEK_DAY_ABBREV },
 
139
        { "WEEK_DAY_NAME",  M_WEEK_DAY_NAME },
 
140
        { "WEEK",           M_WEEK },
 
141
        { "TZOFFSET",       M_TZOFFSET },
 
142
        { "TZ",             M_TZ },
 
143
        { "SYSUPTIME",      M_SYSUPTIME },
 
144
        { "UNIXTIME",       M_UNIXTIME },
 
145
 
 
146
        { "R_DATE",           M_RECVD_OFS + M_DATE },
 
147
        { "R_FULLDATE",       M_RECVD_OFS + M_FULLDATE },
 
148
        { "R_ISODATE",        M_RECVD_OFS + M_ISODATE },
 
149
        { "R_STAMP",          M_RECVD_OFS + M_STAMP },
 
150
        { "R_YEAR",           M_RECVD_OFS + M_YEAR },
 
151
        { "R_YEAR_DAY",       M_RECVD_OFS + M_YEAR_DAY },
 
152
        { "R_MONTH",          M_RECVD_OFS + M_MONTH },
 
153
        { "R_MONTH_WEEK",     M_RECVD_OFS + M_MONTH_WEEK },
 
154
        { "R_MONTH_ABBREV",   M_RECVD_OFS + M_MONTH_ABBREV },
 
155
        { "R_MONTH_NAME",     M_RECVD_OFS + M_MONTH_NAME },
 
156
        { "R_DAY",            M_RECVD_OFS + M_DAY },
 
157
        { "R_HOUR",           M_RECVD_OFS + M_HOUR },
 
158
        { "R_HOUR12",         M_RECVD_OFS + M_HOUR12 },
 
159
        { "R_MIN",            M_RECVD_OFS + M_MIN },
 
160
        { "R_SEC",            M_RECVD_OFS + M_SEC },
 
161
        { "R_MSEC",           M_RECVD_OFS + M_MSEC },
 
162
        { "R_USEC",           M_RECVD_OFS + M_USEC },
 
163
        { "R_AMPM",           M_RECVD_OFS + M_AMPM },
 
164
        { "R_WEEKDAY",        M_RECVD_OFS + M_WEEK_DAY_ABBREV }, /* deprecated */
 
165
        { "R_WEEK_DAY",       M_RECVD_OFS + M_WEEK_DAY },
 
166
        { "R_WEEK_DAY_ABBREV",M_RECVD_OFS + M_WEEK_DAY_ABBREV },
 
167
        { "R_WEEK_DAY_NAME",  M_RECVD_OFS + M_WEEK_DAY_NAME },
 
168
        { "R_WEEK",           M_RECVD_OFS + M_WEEK },
 
169
        { "R_TZOFFSET",       M_RECVD_OFS + M_TZOFFSET },
 
170
        { "R_TZ",             M_RECVD_OFS + M_TZ },
 
171
        { "R_UNIXTIME",       M_RECVD_OFS + M_UNIXTIME },
 
172
 
 
173
        { "S_DATE",           M_STAMP_OFS + M_DATE },
 
174
        { "S_FULLDATE",       M_STAMP_OFS + M_FULLDATE },
 
175
        { "S_ISODATE",        M_STAMP_OFS + M_ISODATE },
 
176
        { "S_STAMP",          M_STAMP_OFS + M_STAMP },
 
177
        { "S_YEAR",           M_STAMP_OFS + M_YEAR },
 
178
        { "S_YEAR_DAY",       M_STAMP_OFS + M_YEAR_DAY },
 
179
        { "S_MONTH",          M_STAMP_OFS + M_MONTH },
 
180
        { "S_MONTH_WEEK",     M_STAMP_OFS + M_MONTH_WEEK },
 
181
        { "S_MONTH_ABBREV",   M_STAMP_OFS + M_MONTH_ABBREV },
 
182
        { "S_MONTH_NAME",     M_STAMP_OFS + M_MONTH_NAME },
 
183
        { "S_DAY",            M_STAMP_OFS + M_DAY },
 
184
        { "S_HOUR",           M_STAMP_OFS + M_HOUR },
 
185
        { "S_HOUR12",         M_STAMP_OFS + M_HOUR12 },
 
186
        { "S_MIN",            M_STAMP_OFS + M_MIN },
 
187
        { "S_SEC",            M_STAMP_OFS + M_SEC },
 
188
        { "S_MSEC",           M_STAMP_OFS + M_MSEC },
 
189
        { "S_USEC",           M_STAMP_OFS + M_USEC },
 
190
        { "S_AMPM",           M_STAMP_OFS + M_AMPM },
 
191
        { "S_WEEKDAY",        M_STAMP_OFS + M_WEEK_DAY_ABBREV }, /* deprecated */
 
192
        { "S_WEEK_DAY",       M_STAMP_OFS + M_WEEK_DAY },
 
193
        { "S_WEEK_DAY_ABBREV",M_STAMP_OFS + M_WEEK_DAY_ABBREV },
 
194
        { "S_WEEK_DAY_NAME",  M_STAMP_OFS + M_WEEK_DAY_NAME },
 
195
        { "S_WEEK",           M_STAMP_OFS + M_WEEK },
 
196
        { "S_TZOFFSET",       M_STAMP_OFS + M_TZOFFSET },
 
197
        { "S_TZ",             M_STAMP_OFS + M_TZ },
 
198
        { "S_UNIXTIME",       M_STAMP_OFS + M_UNIXTIME },
 
199
 
 
200
        { "C_DATE",           M_CSTAMP_OFS + M_DATE },
 
201
        { "C_FULLDATE",       M_CSTAMP_OFS + M_FULLDATE },
 
202
        { "C_ISODATE",        M_CSTAMP_OFS + M_ISODATE },
 
203
        { "C_STAMP",          M_CSTAMP_OFS + M_STAMP },
 
204
        { "C_YEAR",           M_CSTAMP_OFS + M_YEAR },
 
205
        { "C_YEAR_DAY",       M_CSTAMP_OFS + M_YEAR_DAY },
 
206
        { "C_MONTH",          M_CSTAMP_OFS + M_MONTH },
 
207
        { "C_MONTH_WEEK",     M_CSTAMP_OFS + M_MONTH_WEEK },
 
208
        { "C_MONTH_ABBREV",   M_CSTAMP_OFS + M_MONTH_ABBREV },
 
209
        { "C_MONTH_NAME",     M_CSTAMP_OFS + M_MONTH_NAME },
 
210
        { "C_DAY",            M_CSTAMP_OFS + M_DAY },
 
211
        { "C_HOUR",           M_CSTAMP_OFS + M_HOUR },
 
212
        { "C_MIN",            M_CSTAMP_OFS + M_MIN },
 
213
        { "C_SEC",            M_CSTAMP_OFS + M_SEC },
 
214
        { "C_WEEKDAY",        M_CSTAMP_OFS + M_WEEK_DAY_ABBREV }, /* deprecated */
 
215
        { "C_WEEK_DAY",       M_CSTAMP_OFS + M_WEEK_DAY },
 
216
        { "C_WEEK_DAY_ABBREV",M_CSTAMP_OFS + M_WEEK_DAY_ABBREV },
 
217
        { "C_WEEK_DAY_NAME",  M_CSTAMP_OFS + M_WEEK_DAY_NAME },
 
218
        { "C_WEEK",           M_CSTAMP_OFS + M_WEEK },
 
219
        { "C_TZOFFSET",       M_CSTAMP_OFS + M_TZOFFSET },
 
220
        { "C_TZ",             M_CSTAMP_OFS + M_TZ },
 
221
        { "C_UNIXTIME",       M_CSTAMP_OFS + M_UNIXTIME },
 
222
 
 
223
        { "SDATA", M_SDATA },
 
224
        { "MSGHDR", M_MSGHDR },
 
225
        { "SOURCEIP", M_SOURCE_IP },
 
226
        { "SEQNUM", M_SEQNUM },
 
227
        { "CONTEXT_ID", M_CONTEXT_ID },
 
228
 
 
229
        /* values that have specific behaviour with older syslog-ng config versions */
 
230
        { "MSG", M_MESSAGE },
 
231
        { "MESSAGE", M_MESSAGE },
 
232
        { "HOST", M_HOST },
 
233
 
 
234
        /* message independent macros */
 
235
        { "LOGHOST", M_LOGHOST },
 
236
        { NULL, 0 }
 
237
};
 
238
 
 
239
GHashTable *macro_hash;
 
240
GTimeVal app_uptime;
 
241
 
 
242
static void
 
243
result_append(GString *result, const gchar *sstr, gssize len, gboolean escape)
 
244
{
 
245
  gint i;
 
246
  const guchar *ustr = (const guchar *) sstr;
 
247
 
 
248
  if (len < 0)
 
249
    len = strlen(sstr);
 
250
 
 
251
  if (escape)
 
252
    {
 
253
      for (i = 0; i < len; i++)
 
254
        {
 
255
          if (ustr[i] == '\'' || ustr[i] == '"' || ustr[i] == '\\')
 
256
            {
 
257
              g_string_append_c(result, '\\');
 
258
              g_string_append_c(result, ustr[i]);
 
259
            }
 
260
          else if (ustr[i] < ' ')
 
261
            {
 
262
              format_uint32_padded(result, 3, '0', 8, ustr[i]);
 
263
            }
 
264
          else
 
265
            g_string_append_c(result, ustr[i]);
 
266
        }
 
267
    }
 
268
  else
 
269
    g_string_append_len(result, sstr, len);
 
270
}
 
271
 
 
272
static void
 
273
result_append_value(GString *result, LogMessage *lm, NVHandle handle, gboolean escape)
 
274
{
 
275
  const gchar *str;
 
276
  gssize len = 0;
 
277
 
 
278
  str = log_msg_get_value(lm, handle, &len);
 
279
  result_append(result, str, len, escape);
 
280
}
 
281
 
 
282
gboolean
 
283
log_macro_expand(GString *result, gint id, gboolean escape, const LogTemplateOptions *opts, gint tz, gint32 seq_num, const gchar *context_id, LogMessage *msg)
 
284
{
 
285
  switch (id)
 
286
    {
 
287
    case M_FACILITY:
 
288
      {
 
289
        /* facility */
 
290
        const char *n;
 
291
 
 
292
        n = syslog_name_lookup_name_by_value(msg->pri & LOG_FACMASK, sl_facilities);
 
293
        if (n)
 
294
          {
 
295
            g_string_append(result, n);
 
296
          }
 
297
        else
 
298
          {
 
299
            format_uint32_padded(result, 0, 0, 16, (msg->pri & LOG_FACMASK) >> 3);
 
300
          }
 
301
        break;
 
302
      }
 
303
    case M_FACILITY_NUM:
 
304
      {
 
305
        format_uint32_padded(result, 0, 0, 10, (msg->pri & LOG_FACMASK) >> 3);
 
306
        break;
 
307
      }
 
308
    case M_LEVEL:
 
309
      {
 
310
        /* level */
 
311
        const char *n;
 
312
 
 
313
        n = syslog_name_lookup_name_by_value(msg->pri & LOG_PRIMASK, sl_levels);
 
314
        if (n)
 
315
          {
 
316
            g_string_append(result, n);
 
317
          }
 
318
        else
 
319
          {
 
320
            format_uint32_padded(result, 0, 0, 10, msg->pri & LOG_PRIMASK);
 
321
          }
 
322
 
 
323
        break;
 
324
      }
 
325
    case M_LEVEL_NUM:
 
326
      {
 
327
        format_uint32_padded(result, 0, 0, 10, msg->pri & LOG_PRIMASK);
 
328
        break;
 
329
      }
 
330
    case M_TAG:
 
331
      {
 
332
        format_uint32_padded(result, 2, '0', 16, msg->pri);
 
333
        break;
 
334
      }
 
335
    case M_TAGS:
 
336
      {
 
337
        log_msg_print_tags(msg, result);
 
338
        break;
 
339
      }
 
340
    case M_BSDTAG:
 
341
      {
 
342
        format_uint32_padded(result, 0, 0, 10, (msg->pri & LOG_PRIMASK));
 
343
        g_string_append_c(result, (((msg->pri & LOG_FACMASK) >> 3) + 'A'));
 
344
        break;
 
345
      }
 
346
    case M_PRI:
 
347
      {
 
348
        format_uint32_padded(result, 0, 0, 10, msg->pri);
 
349
        break;
 
350
      }
 
351
    case M_HOST:
 
352
      {
 
353
        if (msg->flags & LF_CHAINED_HOSTNAME)
 
354
          {
 
355
            /* host */
 
356
            const gchar *p1, *p2;
 
357
            int remaining, length;
 
358
            gssize host_len;
 
359
            const gchar *host = log_msg_get_value(msg, LM_V_HOST, &host_len);
 
360
 
 
361
            p1 = memchr(host, '@', host_len);
 
362
 
 
363
            if (p1)
 
364
              p1++;
 
365
            else
 
366
              p1 = host;
 
367
            remaining = host_len - (p1 - host);
 
368
            p2 = memchr(p1, '/', remaining);
 
369
            length = p2 ? p2 - p1
 
370
              : host_len - (p1 - host);
 
371
 
 
372
            result_append(result, p1, length, escape);
 
373
          }
 
374
        else
 
375
          {
 
376
            result_append_value(result, msg, LM_V_HOST, escape);
 
377
          }
 
378
        break;
 
379
      }
 
380
    case M_SDATA:
 
381
      if (escape)
 
382
        {
 
383
          GString *sdstr = g_string_sized_new(0);
 
384
 
 
385
          log_msg_append_format_sdata(msg, sdstr, seq_num);
 
386
          result_append(result, sdstr->str, sdstr->len, TRUE);
 
387
          g_string_free(sdstr, TRUE);
 
388
        }
 
389
      else
 
390
        {
 
391
          log_msg_append_format_sdata(msg, result, seq_num);
 
392
        }
 
393
      break;
 
394
    case M_MSGHDR:
 
395
      if ((msg->flags & LF_LEGACY_MSGHDR))
 
396
        {
 
397
          /* fast path for now, as most messages come from legacy devices */
 
398
 
 
399
          result_append_value(result, msg, LM_V_LEGACY_MSGHDR, escape);
 
400
        }
 
401
      else
 
402
        {
 
403
          /* message, complete with program name and pid */
 
404
          gssize len;
 
405
 
 
406
          len = result->len;
 
407
          result_append_value(result, msg, LM_V_PROGRAM, escape);
 
408
          if (len != result->len)
 
409
            {
 
410
              const gchar *pid = log_msg_get_value(msg, LM_V_PID, &len);
 
411
              if (len > 0)
 
412
                {
 
413
                  result_append(result, "[", 1, FALSE);
 
414
                  result_append(result, pid, len, escape);
 
415
                  result_append(result, "]", 1, FALSE);
 
416
                }
 
417
              result_append(result, ": ", 2, FALSE);
 
418
            }
 
419
        }
 
420
      break;
 
421
    case M_MESSAGE:
 
422
      if (cfg_is_config_version_older(configuration, 0x0300))
 
423
        log_macro_expand(result, M_MSGHDR, escape, opts, tz, seq_num, context_id, msg);
 
424
      result_append_value(result, msg, LM_V_MESSAGE, escape);
 
425
      break;
 
426
    case M_SOURCE_IP:
 
427
      {
 
428
        gchar *ip;
 
429
 
 
430
        if (msg->saddr && (g_sockaddr_inet_check(msg->saddr) ||
 
431
#if ENABLE_IPV6
 
432
            g_sockaddr_inet6_check(msg->saddr))
 
433
#else
 
434
            0)
 
435
#endif
 
436
           )
 
437
          {
 
438
            gchar buf[MAX_SOCKADDR_STRING];
 
439
 
 
440
            g_sockaddr_format(msg->saddr, buf, sizeof(buf), GSA_ADDRESS_ONLY);
 
441
            ip = buf;
 
442
          }
 
443
        else
 
444
          {
 
445
            ip = "127.0.0.1";
 
446
          }
 
447
        result_append(result, ip, strlen(ip), escape);
 
448
        break;
 
449
      }
 
450
    case M_SEQNUM:
 
451
      {
 
452
        if (seq_num)
 
453
          {
 
454
            format_uint32_padded(result, 0, 0, 10, seq_num);
 
455
          }
 
456
        break;
 
457
      }
 
458
    case M_CONTEXT_ID:
 
459
      {
 
460
        if (context_id)
 
461
          {
 
462
            result_append(result, context_id, strlen(context_id), escape);
 
463
          }
 
464
        break;
 
465
      }
 
466
    case M_LOGHOST:
 
467
      {
 
468
        gsize hname_len;
 
469
        const gchar *hname = get_local_hostname(&hname_len);
 
470
 
 
471
        result_append(result, hname, hname_len, escape);
 
472
        break;
 
473
      }
 
474
    case M_SYSUPTIME:
 
475
      {
 
476
        GTimeVal ct;
 
477
 
 
478
        g_get_current_time(&ct);
 
479
        format_uint64_padded(result, 0, 0, 10, g_time_val_diff(&ct, &app_uptime) / 1000 / 10);
 
480
        break;
 
481
      }
 
482
 
 
483
    default:
 
484
      {
 
485
        /* year, month, day */
 
486
        struct tm *tm, tm_storage;
 
487
        gchar buf[64];
 
488
        gint length;
 
489
        time_t t;
 
490
        LogStamp *stamp, sstamp;
 
491
        glong zone_ofs;
 
492
        guint tmp_hour;
 
493
 
 
494
        if (id >= M_TIME_FIRST && id <= M_TIME_LAST)
 
495
          {
 
496
            stamp = &msg->timestamps[LM_TS_STAMP];
 
497
          }
 
498
        else if (id >= M_TIME_FIRST + M_RECVD_OFS && id <= M_TIME_LAST + M_RECVD_OFS)
 
499
          {
 
500
            id -= M_RECVD_OFS;
 
501
            stamp = &msg->timestamps[LM_TS_RECVD];
 
502
          }
 
503
        else if (id >= M_TIME_FIRST + M_STAMP_OFS && id <= M_TIME_LAST + M_STAMP_OFS)
 
504
          {
 
505
            id -= M_STAMP_OFS;
 
506
            stamp = &msg->timestamps[LM_TS_STAMP];
 
507
          }
 
508
        else if (id >= M_TIME_FIRST + M_CSTAMP_OFS && id <= M_TIME_LAST + M_CSTAMP_OFS)
 
509
          {
 
510
            GTimeVal tv;
 
511
 
 
512
            id -= M_CSTAMP_OFS;
 
513
            cached_g_current_time(&tv);
 
514
            sstamp.tv_sec = tv.tv_sec;
 
515
            sstamp.tv_usec = tv.tv_usec;
 
516
            sstamp.zone_offset = -1;
 
517
            stamp = &sstamp;
 
518
          }
 
519
        else
 
520
          {
 
521
            g_assert_not_reached();
 
522
            break;
 
523
          }
 
524
 
 
525
        /* try to use the following zone values in order:
 
526
         *   destination specific timezone, if one is specified
 
527
         *   message specific timezone, if one is specified
 
528
         *   local timezone
 
529
         */
 
530
        zone_ofs = (opts->time_zone_info[tz] != NULL ? time_zone_info_get_offset(opts->time_zone_info[tz], stamp->tv_sec) : stamp->zone_offset);
 
531
        if (zone_ofs == -1)
 
532
          zone_ofs = stamp->zone_offset;
 
533
 
 
534
        t = stamp->tv_sec + zone_ofs;
 
535
 
 
536
        cached_gmtime(&t, &tm_storage);
 
537
        tm  = &tm_storage;
 
538
 
 
539
        switch (id)
 
540
          {
 
541
          case M_WEEK_DAY_ABBREV:
 
542
            g_string_append_len(result, weekday_names_abbrev[tm->tm_wday], 3);
 
543
            break;
 
544
          case M_WEEK_DAY_NAME:
 
545
            g_string_append(result, weekday_names[tm->tm_wday]);
 
546
            break;
 
547
          case M_WEEK_DAY:
 
548
            format_uint32_padded(result, 0, 0, 10, tm->tm_wday + 1);
 
549
            break;
 
550
          case M_WEEK:
 
551
            format_uint32_padded(result, 2, '0', 10, (tm->tm_yday - (tm->tm_wday - 1 + 7) % 7 + 7) / 7);
 
552
            break;
 
553
          case M_YEAR:
 
554
            format_uint32_padded(result, 4, '0', 10, tm->tm_year + 1900);
 
555
            break;
 
556
          case M_YEAR_DAY:
 
557
            format_uint32_padded(result, 3, '0', 10, tm->tm_yday + 1);
 
558
            break;
 
559
          case M_MONTH:
 
560
            format_uint32_padded(result, 2, '0', 10, tm->tm_mon + 1);
 
561
            break;
 
562
          case M_MONTH_WEEK:
 
563
            format_uint32_padded(result, 0, 0, 10, ((tm->tm_mday / 7) + ((tm->tm_wday > 0) && ((tm->tm_mday % 7) >= tm->tm_wday))));
 
564
            break;
 
565
          case M_MONTH_ABBREV:
 
566
            g_string_append_len(result, month_names_abbrev[tm->tm_mon], 3);
 
567
            break;
 
568
          case M_MONTH_NAME:
 
569
            g_string_append(result, month_names[tm->tm_mon]);
 
570
            break;
 
571
          case M_DAY:
 
572
            format_uint32_padded(result, 2, '0', 10, tm->tm_mday);
 
573
            break;
 
574
          case M_HOUR:
 
575
            format_uint32_padded(result, 2, '0', 10, tm->tm_hour);
 
576
            break;
 
577
          case M_HOUR12:
 
578
            if (tm->tm_hour < 12)
 
579
              tmp_hour = tm->tm_hour;
 
580
            else
 
581
              tmp_hour = tm->tm_hour - 12;
 
582
 
 
583
            if (tmp_hour == 0)
 
584
              tmp_hour = 12;
 
585
            format_uint32_padded(result, 2, '0', 10, tmp_hour);
 
586
            break;
 
587
          case M_MIN:
 
588
            format_uint32_padded(result, 2, '0', 10, tm->tm_min);
 
589
            break;
 
590
          case M_SEC:
 
591
            format_uint32_padded(result, 2, '0', 10, tm->tm_sec);
 
592
            break;
 
593
          case M_MSEC:
 
594
            format_uint32_padded(result, 3, '0', 10, stamp->tv_usec/1000);
 
595
            break;
 
596
          case M_USEC:
 
597
            format_uint32_padded(result, 6, '0', 10, stamp->tv_usec);
 
598
            break;
 
599
          case M_AMPM:
 
600
            g_string_append(result, tm->tm_hour < 12 ? "AM" : "PM");
 
601
            break;
 
602
          case M_DATE:
 
603
          case M_STAMP:
 
604
          case M_ISODATE:
 
605
          case M_FULLDATE:
 
606
          case M_UNIXTIME:
 
607
            {
 
608
              gint format = id == M_DATE ? TS_FMT_BSD :
 
609
                            id == M_ISODATE ? TS_FMT_ISO :
 
610
                            id == M_FULLDATE ? TS_FMT_FULL :
 
611
                            id == M_UNIXTIME ? TS_FMT_UNIX :
 
612
                            opts->ts_format;
 
613
 
 
614
              log_stamp_append_format(stamp, result, format, zone_ofs, opts->frac_digits);
 
615
              break;
 
616
            }
 
617
          case M_TZ:
 
618
          case M_TZOFFSET:
 
619
            length = format_zone_info(buf, sizeof(buf), zone_ofs);
 
620
            g_string_append_len(result, buf, length);
 
621
            break;
 
622
          }
 
623
        break;
 
624
      }
 
625
    }
 
626
  return TRUE;
 
627
}
 
628
 
 
629
guint
 
630
log_macro_lookup(gchar *macro, gint len)
 
631
{
 
632
  gchar buf[256];
 
633
  gint macro_id;
 
634
 
 
635
  g_assert(macro_hash);
 
636
  g_strlcpy(buf, macro, MIN(sizeof(buf), len+1));
 
637
  macro_id = GPOINTER_TO_INT(g_hash_table_lookup(macro_hash, buf));
 
638
 
 
639
  if (cfg_is_config_version_older(configuration, 0x0300) && (macro_id == M_MESSAGE))
 
640
    {
 
641
      static gboolean msg_macro_warning = FALSE;
 
642
 
 
643
      if (!msg_macro_warning)
 
644
        {
 
645
          msg_warning("WARNING: template: the meaning of the $MSG/$MESSAGE macros has changed from " VERSION_3_0 ", please prepend a $MSGHDR when upgrading to " VERSION_3_0 " config format", NULL);
 
646
          msg_macro_warning = TRUE;
 
647
        }
 
648
    }
 
649
  return macro_id;
 
650
}
 
651
 
 
652
typedef struct
 
653
{
 
654
  LogTemplate *template;
 
655
  GList *result;
 
656
  gchar *cursor;
 
657
  GString *text;
 
658
  gint msg_ref;
 
659
} LogTemplateCompiler;
 
660
 
 
661
enum
 
662
{
 
663
  LTE_MACRO,
 
664
  LTE_VALUE,
 
665
  LTE_FUNC
 
666
};
 
667
 
 
668
typedef struct _LogTemplateElem
 
669
{
 
670
  gsize text_len;
 
671
  gchar *text;
 
672
  gchar *default_value;
 
673
  guint16 msg_ref;
 
674
  guint8 type;
 
675
  union
 
676
  {
 
677
    guint macro;
 
678
    NVHandle value_handle;
 
679
    struct
 
680
    {
 
681
      LogTemplateFunction *ops;
 
682
      gpointer state;
 
683
    } func;
 
684
  };
 
685
} LogTemplateElem;
 
686
 
 
687
 
 
688
/* simple template functions which take templates as arguments */
 
689
 
 
690
gboolean
 
691
tf_simple_func_prepare(LogTemplateFunction *self, gpointer s, LogTemplate *parent, gint argc, gchar *argv[], GError **error)
 
692
{
 
693
  TFSimpleFuncState *state = (TFSimpleFuncState *) s;
 
694
  gint i;
 
695
 
 
696
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
 
697
  state->argv = g_malloc(sizeof(LogTemplate *) * (argc - 1));
 
698
 
 
699
  /* NOTE: the argv argument con tains the function name as argv[0],
 
700
   * but the LogTemplate array doesn't. Thus the index is shifted by
 
701
   * one. */
 
702
  for (i = 0; i < argc - 1; i++)
 
703
    {
 
704
      state->argv[i] = log_template_new(parent->cfg, NULL);
 
705
      log_template_set_escape(state->argv[i], parent->escape);
 
706
      if (!log_template_compile(state->argv[i], argv[i + 1], error))
 
707
        goto error;
 
708
    }
 
709
  state->argc = argc - 1;
 
710
  return TRUE;
 
711
 error:
 
712
  return FALSE;
 
713
}
 
714
 
 
715
void
 
716
tf_simple_func_eval(LogTemplateFunction *self, gpointer s, const LogTemplateInvokeArgs *args)
 
717
{
 
718
  TFSimpleFuncState *state = (TFSimpleFuncState *) s;
 
719
  gint i;
 
720
 
 
721
  for (i = 0; i < state->argc; i++)
 
722
    {
 
723
      GString **arg;
 
724
 
 
725
      if (args->bufs->len <= i)
 
726
        g_ptr_array_add(args->bufs, g_string_sized_new(256));
 
727
 
 
728
      arg = (GString **) &g_ptr_array_index(args->bufs, i);
 
729
      g_string_truncate(*arg, 0);
 
730
 
 
731
      log_template_append_format_recursive(state->argv[i], args, *arg);
 
732
    }
 
733
}
 
734
 
 
735
void
 
736
tf_simple_func_call(LogTemplateFunction *self, gpointer s, const LogTemplateInvokeArgs *args, GString *result)
 
737
{
 
738
  TFSimpleFunc simple_func = (TFSimpleFunc) self->arg;
 
739
  TFSimpleFuncState *state = (TFSimpleFuncState *) s;
 
740
 
 
741
  simple_func(args->messages[args->num_messages-1], state->argc, (GString **) args->bufs->pdata, result);
 
742
}
 
743
 
 
744
void
 
745
tf_simple_func_free_state(gpointer s)
 
746
{
 
747
  TFSimpleFuncState *state = (TFSimpleFuncState *) s;
 
748
  gint i;
 
749
 
 
750
  for (i = 0; i < state->argc; i++)
 
751
    {
 
752
      if (state->argv[i])
 
753
        log_template_unref(state->argv[i]);
 
754
    }
 
755
  g_free(state->argv);
 
756
}
 
757
 
 
758
static void
 
759
log_template_add_macro_elem(LogTemplateCompiler *self, guint macro, gchar *default_value)
 
760
{
 
761
  LogTemplateElem *e;
 
762
 
 
763
  e = g_new0(LogTemplateElem, 1);
 
764
  e->type = LTE_MACRO;
 
765
  e->text_len = self->text ? self->text->len : 0;
 
766
  e->text = self->text ? g_strndup(self->text->str, self->text->len) : NULL;
 
767
  e->macro = macro;
 
768
  e->default_value = default_value;
 
769
  e->msg_ref = self->msg_ref;
 
770
  self->result = g_list_prepend(self->result, e);
 
771
}
 
772
 
 
773
static void
 
774
log_template_add_value_elem(LogTemplateCompiler *self, gchar *value_name, gsize value_name_len, gchar *default_value)
 
775
{
 
776
  LogTemplateElem *e;
 
777
  gchar *dup;
 
778
 
 
779
  e = g_new0(LogTemplateElem, 1);
 
780
  e->type = LTE_VALUE;
 
781
  e->text_len = self->text ? self->text->len : 0;
 
782
  e->text = self->text ? g_strndup(self->text->str, self->text->len) : NULL;
 
783
  /* value_name is not NUL terminated */
 
784
  dup = g_strndup(value_name, value_name_len);
 
785
  e->value_handle = log_msg_get_value_handle(dup);
 
786
  g_free(dup);
 
787
  e->default_value = default_value;
 
788
  e->msg_ref = self->msg_ref;
 
789
  self->result = g_list_prepend(self->result, e);
 
790
}
 
791
 
 
792
 
 
793
/* NOTE: this steals argv if successful */
 
794
static gboolean
 
795
log_template_add_func_elem(LogTemplateCompiler *self, gint argc, gchar *argv[], GError **error)
 
796
{
 
797
  LogTemplateElem *e;
 
798
  Plugin *p;
 
799
  gchar *argv_copy[argc + 1];
 
800
 
 
801
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
 
802
 
 
803
  if (argc == 0)
 
804
    return TRUE;
 
805
 
 
806
  e = g_malloc0(sizeof(LogTemplateElem) + (argc - 1) * sizeof(LogTemplate *));
 
807
  e->type = LTE_FUNC;
 
808
  e->text_len = self->text ? self->text->len : 0;
 
809
  e->text = self->text ? g_strndup(self->text->str, self->text->len) : NULL;
 
810
  e->msg_ref = self->msg_ref;
 
811
 
 
812
  p = plugin_find(self->template->cfg, LL_CONTEXT_TEMPLATE_FUNC, argv[0]);
 
813
  if (!p)
 
814
    {
 
815
      g_set_error(error, LOG_TEMPLATE_ERROR, LOG_TEMPLATE_ERROR_COMPILE, "Unknown template function %s", argv[0]);
 
816
      goto error;
 
817
    }
 
818
  else
 
819
    {
 
820
      e->func.ops = plugin_construct(p, self->template->cfg, LL_CONTEXT_TEMPLATE_FUNC, argv[0]);
 
821
    }
 
822
 
 
823
  e->func.state = g_malloc0(e->func.ops->size_of_state);
 
824
 
 
825
  /* prepare may modify the argv array: remove and rearrange elements */
 
826
  memcpy(argv_copy, argv, (argc + 1) * sizeof(argv[0]));
 
827
  if (!e->func.ops->prepare(e->func.ops, e->func.state, self->template, argc, argv_copy, error))
 
828
    {
 
829
      e->func.ops->free_state(e->func.state);
 
830
      g_free(e->func.state);
 
831
      goto error;
 
832
    }
 
833
  g_strfreev(argv);
 
834
  self->result = g_list_prepend(self->result, e);
 
835
  return TRUE;
 
836
 
 
837
 error:
 
838
  if (e->text)
 
839
    g_free(e->text);
 
840
  g_free(e);
 
841
  return FALSE;
 
842
}
 
843
 
 
844
static void
 
845
log_template_elem_free(LogTemplateElem *e)
 
846
{
 
847
  switch (e->type)
 
848
    {
 
849
    case LTE_FUNC:
 
850
      if (e->func.state)
 
851
        {
 
852
          e->func.ops->free_state(e->func.state);
 
853
          g_free(e->func.state);
 
854
        }
 
855
      break;
 
856
    }
 
857
  if (e->default_value)
 
858
    g_free(e->default_value);
 
859
  if (e->text)
 
860
    g_free(e->text);
 
861
  g_free(e);
 
862
}
 
863
 
 
864
static void
 
865
log_template_elem_free_list(GList *el)
 
866
{
 
867
  for (; el; el = el->next)
 
868
    {
 
869
      log_template_elem_free((LogTemplateElem *) el->data);
 
870
    }
 
871
  g_list_free(el);
 
872
}
 
873
 
 
874
static void
 
875
parse_msg_ref(LogTemplateCompiler *self)
 
876
{
 
877
  self->msg_ref = 0;
 
878
  if ((*self->cursor) == '@')
 
879
    {
 
880
      self->cursor++;
 
881
      if ((*self->cursor) >= '0' && (*self->cursor) <= '9')
 
882
        {
 
883
          /* syntax: ${name}@1 to denote the log message index in the correllation state */
 
884
          while ((*self->cursor) >= '0' && (*self->cursor) <= '9')
 
885
            {
 
886
              self->msg_ref += self->msg_ref * 10 + ((*self->cursor) - '0');
 
887
              self->cursor++;
 
888
            }
 
889
          self->msg_ref += 1;
 
890
        }
 
891
      else
 
892
        {
 
893
          if ((*self->cursor) != '@')
 
894
            {
 
895
              msg_warning("Non-numeric correlation state ID found, assuming a literal '@' character. To avoid confusion when using a literal '@' after a macro or template function, write '@@' in the template.",
 
896
                          evt_tag_str("Template", self->template->template),
 
897
                          NULL);
 
898
              self->cursor--;
 
899
            }
 
900
          self->msg_ref = 0;
 
901
        }
 
902
    }
 
903
}
 
904
 
 
905
static void
 
906
log_template_compiler_fill_compile_error(GError **error, const gchar *error_info, gint error_pos)
 
907
{
 
908
  g_set_error(error, LOG_TEMPLATE_ERROR, LOG_TEMPLATE_ERROR_COMPILE, "%s, error_pos='%d'", error_info, error_pos);
 
909
}
 
910
 
 
911
static void
 
912
log_template_compiler_append_and_increment(LogTemplateCompiler *self, GString *text)
 
913
{
 
914
  g_string_append_c(text, *self->cursor);
 
915
  self->cursor++;
 
916
}
 
917
 
 
918
static gint
 
919
log_template_compiler_get_macro_length(gchar *start, gchar *end, gchar **token)
 
920
{
 
921
  gint result = 0;
 
922
  gchar *colon = memchr(start, ':', end - start - 1);
 
923
  if (colon)
 
924
    {
 
925
      result = colon - start;
 
926
      *token = colon < end ? colon + 1 : NULL;
 
927
    }
 
928
  else
 
929
    {
 
930
      result = end - start - 1;
 
931
      *token = NULL;
 
932
    }
 
933
  return result;
 
934
}
 
935
 
 
936
 
 
937
static gchar *
 
938
log_template_compiler_get_default_value(LogTemplateCompiler *self, gchar *token)
 
939
{
 
940
  g_assert(token);
 
941
  if (*token != '-')
 
942
    {
 
943
      return NULL;
 
944
    }
 
945
  return g_strndup(token + 1, self->cursor - token - 2);
 
946
}
 
947
 
 
948
static inline gboolean
 
949
is_macro_name(gchar c)
 
950
{
 
951
  return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_') || (c >= '0' && c <= '9');
 
952
}
 
953
 
 
954
#define STEP_BY_TRUE(p, x) while(x) p++;
 
955
 
 
956
static void
 
957
log_template_compiler_add_elem(LogTemplateCompiler *self, gchar *start, gint macro_len, gchar *default_value)
 
958
{
 
959
  gint macro = log_macro_lookup(start, macro_len);
 
960
  if (macro == M_NONE)
 
961
    {
 
962
      log_template_add_value_elem(self, start, macro_len, default_value);
 
963
    }
 
964
  else
 
965
    {
 
966
      log_template_add_macro_elem(self, macro, default_value);
 
967
    }
 
968
}
 
969
 
 
970
static gboolean
 
971
log_template_compiler_process_braced_template(LogTemplateCompiler *self, GError **error)
 
972
{
 
973
  gint macro_len;
 
974
  gchar *default_value = NULL;
 
975
  gchar *start;
 
976
  gchar *end;
 
977
  gchar *token;
 
978
 
 
979
  self->cursor++;
 
980
  start = self->cursor;
 
981
  end = strchr(self->cursor, '}');
 
982
 
 
983
  if (!end)
 
984
    {
 
985
      log_template_compiler_fill_compile_error(error, "Invalid macro, '}' is missing", strlen(self->template->template));
 
986
      return FALSE;
 
987
    }
 
988
 
 
989
  self->cursor = end + 1;
 
990
  macro_len = log_template_compiler_get_macro_length(start, self->cursor, &token);
 
991
  if (token)
 
992
    {
 
993
      default_value = log_template_compiler_get_default_value(self, token);
 
994
      if (!default_value)
 
995
        {
 
996
          log_template_compiler_fill_compile_error(error, "Unknown substitution function", token - self->template->template);
 
997
          return FALSE;
 
998
        }
 
999
    }
 
1000
  parse_msg_ref(self);
 
1001
  log_template_compiler_add_elem(self, start, macro_len, default_value);
 
1002
  return TRUE;
 
1003
}
 
1004
 
 
1005
static gboolean
 
1006
log_template_compiler_add_quoted_string(LogTemplateCompiler *self, gboolean is_top_level, GString *result)
 
1007
{
 
1008
  gchar *quote = self->cursor;
 
1009
  gchar *end_of_quote = strchr(quote + 1, *quote);
 
1010
  if (!end_of_quote)
 
1011
    {
 
1012
      return FALSE;
 
1013
    }
 
1014
  self->cursor = end_of_quote + 1;
 
1015
  if (is_top_level)
 
1016
    {
 
1017
      /* skip the quote in top-level and don't skip in expressions enclosed in parens */
 
1018
      quote++;
 
1019
    }
 
1020
  else
 
1021
    {
 
1022
      end_of_quote++;
 
1023
    }
 
1024
  g_string_append_len(result, quote, end_of_quote - quote);
 
1025
  return TRUE;
 
1026
}
 
1027
 
 
1028
static gboolean
 
1029
log_template_compiler_process_arg_list(LogTemplateCompiler *self, GPtrArray *result)
 
1030
{
 
1031
  GString *arg_buf = g_string_sized_new(32);
 
1032
  gint parens = 1;
 
1033
  self->cursor++;
 
1034
 
 
1035
  while (*self->cursor && *self->cursor == ' ')
 
1036
    self->cursor++;
 
1037
 
 
1038
  while(*self->cursor)
 
1039
    {
 
1040
      if (*self->cursor == '\\')
 
1041
        {
 
1042
          self->cursor++;
 
1043
        }
 
1044
      else if (*self->cursor == '(')
 
1045
        {
 
1046
          parens++;
 
1047
        }
 
1048
      else if (*self->cursor == ')')
 
1049
        {
 
1050
          parens--;
 
1051
          if (parens == 0)
 
1052
            {
 
1053
              break;
 
1054
            }
 
1055
        }
 
1056
      else if (*self->cursor == '"' || *self->cursor == '\'')
 
1057
        {
 
1058
          if (!log_template_compiler_add_quoted_string(self, parens == 1, arg_buf))
 
1059
            {
 
1060
              g_ptr_array_add(result, NULL);
 
1061
              g_string_free(arg_buf, TRUE);
 
1062
              return FALSE;
 
1063
            }
 
1064
          continue;
 
1065
        }
 
1066
      else if (parens == 1 && (*self->cursor == ' ' || *self->cursor == '\t'))
 
1067
        {
 
1068
          g_ptr_array_add(result, g_strndup(arg_buf->str, arg_buf->len));
 
1069
          g_string_truncate(arg_buf, 0);
 
1070
          while (*self->cursor && (*self->cursor == ' ' || *self->cursor == '\t'))
 
1071
            self->cursor++;
 
1072
          continue;
 
1073
        }
 
1074
      log_template_compiler_append_and_increment(self, arg_buf);
 
1075
    }
 
1076
  if (arg_buf->len > 0)
 
1077
    {
 
1078
      g_ptr_array_add(result, g_strndup(arg_buf->str, arg_buf->len));
 
1079
    }
 
1080
  g_ptr_array_add(result, NULL);
 
1081
  g_string_free(arg_buf, TRUE);
 
1082
  return *self->cursor == ')';
 
1083
}
 
1084
 
 
1085
static gboolean
 
1086
log_template_compiler_process_template_function(LogTemplateCompiler *self, GError **error)
 
1087
{
 
1088
  GPtrArray *strv = g_ptr_array_new();
 
1089
 
 
1090
  if (!log_template_compiler_process_arg_list(self, strv))
 
1091
    {
 
1092
      log_template_compiler_fill_compile_error(error, "Invalid template function reference, missing function name or inbalanced '('", self->cursor - self->template->template);
 
1093
      goto error;
 
1094
    }
 
1095
  self->cursor++;
 
1096
 
 
1097
  parse_msg_ref(self);
 
1098
  if (!log_template_add_func_elem(self, strv->len - 1, (gchar **) strv->pdata, error))
 
1099
    {
 
1100
      goto error;
 
1101
    }
 
1102
  g_ptr_array_free(strv, FALSE);
 
1103
  return TRUE;
 
1104
error:
 
1105
  g_strfreev((gchar **)strv->pdata);
 
1106
  g_ptr_array_free(strv, FALSE);
 
1107
  return FALSE;
 
1108
}
 
1109
 
 
1110
static void
 
1111
log_template_compiler_process_unbraced_template(LogTemplateCompiler *self)
 
1112
{
 
1113
  gchar *start = self->cursor;
 
1114
  gint macro_len;
 
1115
  do
 
1116
    {
 
1117
      self->cursor++;
 
1118
    }
 
1119
  while (is_macro_name(*self->cursor));
 
1120
  macro_len = self->cursor - start;
 
1121
  log_template_compiler_add_elem(self, start, macro_len, NULL);
 
1122
}
 
1123
 
 
1124
static gboolean
 
1125
log_template_compiler_process_value(LogTemplateCompiler *self, GError **error)
 
1126
{
 
1127
  gboolean finished = FALSE;
 
1128
  gchar p;
 
1129
  self->cursor++;
 
1130
  p = *self->cursor;
 
1131
  /* macro reference */
 
1132
  if (p == '{')
 
1133
    {
 
1134
      if (!log_template_compiler_process_braced_template(self, error))
 
1135
        {
 
1136
          return FALSE;
 
1137
        }
 
1138
      finished = TRUE;
 
1139
    }
 
1140
  /* template function */
 
1141
  else if (p == '(')
 
1142
    {
 
1143
      if (!log_template_compiler_process_template_function(self, error))
 
1144
        {
 
1145
          return FALSE;
 
1146
        }
 
1147
      finished = TRUE;
 
1148
    }
 
1149
  /* unbraced macro */
 
1150
  else if (is_macro_name(p))
 
1151
    {
 
1152
      log_template_compiler_process_unbraced_template(self);
 
1153
      finished = TRUE;
 
1154
    }
 
1155
  /* escaped value with dollar */
 
1156
  else
 
1157
    {
 
1158
      if (p != '$')
 
1159
        {
 
1160
          g_string_append_c(self->text, '$');
 
1161
        }
 
1162
      if (p)
 
1163
        {
 
1164
          log_template_compiler_append_and_increment(self, self->text);
 
1165
        }
 
1166
    }
 
1167
  if (finished)
 
1168
    {
 
1169
      g_string_truncate(self->text, 0);
 
1170
    }
 
1171
  return TRUE;
 
1172
}
 
1173
 
 
1174
gboolean
 
1175
log_template_compiler_process_token(LogTemplateCompiler *self, GError **error)
 
1176
{
 
1177
  self->msg_ref = 0;
 
1178
  if (*self->cursor == '$')
 
1179
    {
 
1180
      return log_template_compiler_process_value(self, error);
 
1181
    }
 
1182
  if (*self->cursor == '\\')
 
1183
    {
 
1184
      if (cfg_is_config_version_older(self->template->cfg, 0x305))
 
1185
        {
 
1186
          msg_warning("Template escaping changed in version 3.5. Use '$$' to specify a literal dollar sign instead of '\\$' and remove the escaping of the backslash character when you upgrade your configuration",
 
1187
                      evt_tag_str("Template", self->template->template),
 
1188
                      NULL);
 
1189
          self->cursor++;
 
1190
        }
 
1191
 
 
1192
    }
 
1193
  if (*self->cursor)
 
1194
    {
 
1195
      log_template_compiler_append_and_increment(self, self->text);
 
1196
    }
 
1197
  return TRUE;
 
1198
}
 
1199
 
 
1200
static void
 
1201
log_template_compiler_free_result(LogTemplateCompiler *self)
 
1202
{
 
1203
  log_template_elem_free_list(self->result);
 
1204
  self->result = NULL;
 
1205
}
 
1206
 
 
1207
static gboolean
 
1208
log_template_compiler_compile(LogTemplateCompiler *self, GList **compiled_template, GError **error)
 
1209
{
 
1210
  gboolean result = FALSE;
 
1211
 
 
1212
  while (*self->cursor)
 
1213
    {
 
1214
      if (!log_template_compiler_process_token(self, error))
 
1215
        {
 
1216
          log_template_compiler_free_result(self);
 
1217
          g_string_sprintf(self->text, "error in template: %s", self->template->template);
 
1218
          log_template_add_macro_elem(self, M_NONE, NULL);
 
1219
          goto error;
 
1220
        }
 
1221
    }
 
1222
  if (self->text->len)
 
1223
    {
 
1224
      log_template_add_macro_elem(self, M_NONE, NULL);
 
1225
    }
 
1226
  result = TRUE;
 
1227
 error:
 
1228
  *compiled_template = g_list_reverse(self->result);
 
1229
  self->result = NULL;
 
1230
  return result;
 
1231
}
 
1232
 
 
1233
static void
 
1234
log_template_compiler_init(LogTemplateCompiler *self, LogTemplate *template)
 
1235
{
 
1236
  memset(self, 0, sizeof(*self));
 
1237
 
 
1238
  self->template = log_template_ref(template);
 
1239
  self->cursor = self->template->template;
 
1240
  self->text = g_string_sized_new(32);
 
1241
}
 
1242
 
 
1243
static void
 
1244
log_template_compiler_clear(LogTemplateCompiler *self)
 
1245
{
 
1246
  log_template_unref(self->template);
 
1247
  g_string_free(self->text, TRUE);
 
1248
}
 
1249
 
 
1250
static void
 
1251
log_template_reset_compiled(LogTemplate *self)
 
1252
{
 
1253
  log_template_elem_free_list(self->compiled_template);
 
1254
  self->compiled_template = NULL;
 
1255
}
 
1256
 
 
1257
gboolean
 
1258
log_template_compile(LogTemplate *self, const gchar *template, GError **error)
 
1259
{
 
1260
  LogTemplateCompiler compiler;
 
1261
  gboolean result;
 
1262
 
 
1263
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
 
1264
 
 
1265
  log_template_reset_compiled(self);
 
1266
  if (self->template)
 
1267
    g_free(self->template);
 
1268
  self->template = g_strdup(template);
 
1269
 
 
1270
  log_template_compiler_init(&compiler, self);
 
1271
  result = log_template_compiler_compile(&compiler, &self->compiled_template, error);
 
1272
  log_template_compiler_clear(&compiler);
 
1273
  return result;
 
1274
}
 
1275
 
 
1276
void
 
1277
log_template_set_escape(LogTemplate *self, gboolean enable)
 
1278
{
 
1279
  self->escape = enable;
 
1280
}
 
1281
 
 
1282
gboolean
 
1283
log_template_set_type_hint(LogTemplate *self, const gchar *type_hint, GError **error)
 
1284
{
 
1285
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
 
1286
 
 
1287
  return type_hint_parse(type_hint, &self->type_hint, error);
 
1288
}
 
1289
 
 
1290
 
 
1291
void
 
1292
log_template_append_format_with_context(LogTemplate *self, LogMessage **messages, gint num_messages, const LogTemplateOptions *opts, gint tz, gint32 seq_num, const gchar *context_id, GString *result)
 
1293
{
 
1294
  GList *p;
 
1295
  LogTemplateElem *e;
 
1296
 
 
1297
  if (!opts)
 
1298
    opts = &self->cfg->template_options;
 
1299
 
 
1300
  for (p = self->compiled_template; p; p = g_list_next(p))
 
1301
    {
 
1302
      gint msg_ndx;
 
1303
 
 
1304
      e = (LogTemplateElem *) p->data;
 
1305
      if (e->text)
 
1306
        {
 
1307
          g_string_append_len(result, e->text, e->text_len);
 
1308
        }
 
1309
 
 
1310
      /* NOTE: msg_ref is 1 larger than the index specified by the user in
 
1311
       * order to make it distinguishable from the zero value.  Therefore
 
1312
       * the '>' instead of '>='
 
1313
       *
 
1314
       * msg_ref == 0 means that the user didn't specify msg_ref
 
1315
       * msg_ref >= 1 means that the user supplied the given msg_ref, 1 is equal to @0 */
 
1316
      if (e->msg_ref > num_messages)
 
1317
        continue;
 
1318
      msg_ndx = num_messages - e->msg_ref;
 
1319
 
 
1320
      /* value and macro can't understand a context, assume that no msg_ref means @0 */
 
1321
      if (e->msg_ref == 0)
 
1322
        msg_ndx--;
 
1323
 
 
1324
      switch (e->type)
 
1325
        {
 
1326
        case LTE_VALUE:
 
1327
          {
 
1328
            const gchar *value = NULL;
 
1329
            gssize value_len = -1;
 
1330
 
 
1331
            value = log_msg_get_value(messages[msg_ndx], e->value_handle, &value_len);
 
1332
            if (value && value[0])
 
1333
              result_append(result, value, value_len, self->escape);
 
1334
            else if (e->default_value)
 
1335
              result_append(result, e->default_value, -1, self->escape);
 
1336
            break;
 
1337
          }
 
1338
        case LTE_MACRO:
 
1339
          {
 
1340
            gint len = result->len;
 
1341
 
 
1342
            if (e->macro)
 
1343
              {
 
1344
                log_macro_expand(result, e->macro, self->escape, opts ? opts : &self->cfg->template_options, tz, seq_num, context_id, messages[msg_ndx]);
 
1345
                if (len == result->len && e->default_value)
 
1346
                  g_string_append(result, e->default_value);
 
1347
              }
 
1348
            break;
 
1349
          }
 
1350
        case LTE_FUNC:
 
1351
          {
 
1352
            g_static_mutex_lock(&self->arg_lock);
 
1353
            if (!self->arg_bufs)
 
1354
              self->arg_bufs = g_ptr_array_sized_new(0);
 
1355
 
 
1356
            if (1)
 
1357
              {
 
1358
                LogTemplateInvokeArgs args =
 
1359
                  {
 
1360
                    self->arg_bufs,
 
1361
                    e->msg_ref ? &messages[msg_ndx] : messages,
 
1362
                    e->msg_ref ? 1 : num_messages,
 
1363
                    opts,
 
1364
                    tz,
 
1365
                    seq_num,
 
1366
                    context_id
 
1367
                  };
 
1368
 
 
1369
 
 
1370
                /* if a function call is called with an msg_ref, we only
 
1371
                 * pass that given logmsg to argument resolution, otherwise
 
1372
                 * we pass the whole set so the arguments can individually
 
1373
                 * specify which message they want to resolve from
 
1374
                 */
 
1375
                if (e->func.ops->eval)
 
1376
                  e->func.ops->eval(e->func.ops, e->func.state, &args);
 
1377
                e->func.ops->call(e->func.ops, e->func.state, &args, result);
 
1378
              }
 
1379
            g_static_mutex_unlock(&self->arg_lock);
 
1380
            break;
 
1381
          }
 
1382
        }
 
1383
    }
 
1384
}
 
1385
 
 
1386
void
 
1387
log_template_append_format_recursive(LogTemplate *self, const LogTemplateInvokeArgs *args, GString *result)
 
1388
{
 
1389
  log_template_append_format_with_context(self,
 
1390
                                          args->messages, args->num_messages,
 
1391
                                          args->opts, args->tz, args->seq_num, args->context_id, result);
 
1392
}
 
1393
 
 
1394
void
 
1395
log_template_format_with_context(LogTemplate *self, LogMessage **messages, gint num_messages, const LogTemplateOptions *opts, gint tz, gint32 seq_num, const gchar *context_id, GString *result)
 
1396
{
 
1397
  g_string_truncate(result, 0);
 
1398
  log_template_append_format_with_context(self, messages, num_messages, opts, tz, seq_num, context_id, result);
 
1399
}
 
1400
 
 
1401
void
 
1402
log_template_append_format(LogTemplate *self, LogMessage *lm, const LogTemplateOptions *opts, gint tz, gint32 seq_num, const gchar *context_id, GString *result)
 
1403
{
 
1404
  log_template_append_format_with_context(self, &lm, 1, opts, tz, seq_num, context_id, result);
 
1405
}
 
1406
 
 
1407
void
 
1408
log_template_format(LogTemplate *self, LogMessage *lm, const LogTemplateOptions *opts, gint tz, gint32 seq_num, const gchar *context_id, GString *result)
 
1409
{
 
1410
  g_string_truncate(result, 0);
 
1411
  log_template_append_format(self, lm, opts, tz, seq_num, context_id, result);
 
1412
}
 
1413
 
 
1414
LogTemplate *
 
1415
log_template_new(GlobalConfig *cfg, gchar *name)
 
1416
{
 
1417
  LogTemplate *self = g_new0(LogTemplate, 1);
 
1418
 
 
1419
  self->name = g_strdup(name);
 
1420
  self->ref_cnt = 1;
 
1421
  self->cfg = cfg;
 
1422
  g_static_mutex_init(&self->arg_lock);
 
1423
  if (cfg_is_config_version_older(cfg, 0x0300))
 
1424
    {
 
1425
      static gboolean warn_written = FALSE;
 
1426
 
 
1427
      if (!warn_written)
 
1428
        {
 
1429
          msg_warning("WARNING: template: the default value for template-escape has changed to 'no' from " VERSION_3_0 ", please update your configuration file accordingly",
 
1430
                      NULL);
 
1431
          warn_written = TRUE;
 
1432
        }
 
1433
      self->escape = TRUE;
 
1434
    }
 
1435
  return self;
 
1436
}
 
1437
 
 
1438
static void
 
1439
log_template_free(LogTemplate *self)
 
1440
{
 
1441
  if (self->arg_bufs)
 
1442
    {
 
1443
      gint i;
 
1444
 
 
1445
      for (i = 0; i < self->arg_bufs->len; i++)
 
1446
        g_string_free(g_ptr_array_index(self->arg_bufs, i), TRUE);
 
1447
      g_ptr_array_free(self->arg_bufs, TRUE);
 
1448
    }
 
1449
  log_template_reset_compiled(self);
 
1450
  g_free(self->name);
 
1451
  g_free(self->template);
 
1452
  g_static_mutex_free(&self->arg_lock);
 
1453
  g_free(self);
 
1454
}
 
1455
 
 
1456
LogTemplate *
 
1457
log_template_ref(LogTemplate *s)
 
1458
{
 
1459
  if (s)
 
1460
    {
 
1461
      g_assert(s->ref_cnt > 0);
 
1462
      s->ref_cnt++;
 
1463
    }
 
1464
  return s;
 
1465
}
 
1466
 
 
1467
void
 
1468
log_template_unref(LogTemplate *s)
 
1469
{
 
1470
  if (s)
 
1471
    {
 
1472
      g_assert(s->ref_cnt > 0);
 
1473
      if (--s->ref_cnt == 0)
 
1474
        log_template_free(s);
 
1475
    }
 
1476
}
 
1477
 
 
1478
/* NOTE: _init needs to be idempotent when called multiple times w/o invoking _destroy */
 
1479
void
 
1480
log_template_options_init(LogTemplateOptions *options, GlobalConfig *cfg)
 
1481
{
 
1482
  gint i;
 
1483
 
 
1484
  if (options->initialized)
 
1485
    return;
 
1486
  if (options->ts_format == -1)
 
1487
    options->ts_format = cfg->template_options.ts_format;
 
1488
  for (i = 0; i < LTZ_MAX; i++)
 
1489
    {
 
1490
      if (options->time_zone[i] == NULL)
 
1491
        options->time_zone[i] = g_strdup(cfg->template_options.time_zone[i]);
 
1492
      if (options->time_zone_info[i] == NULL)
 
1493
        options->time_zone_info[i] = time_zone_info_new(options->time_zone[i]);
 
1494
    }
 
1495
 
 
1496
  if (options->frac_digits == -1)
 
1497
    options->frac_digits = cfg->template_options.frac_digits;
 
1498
  if (options->on_error == -1)
 
1499
    options->on_error = cfg->template_options.on_error;
 
1500
  options->initialized = TRUE;
 
1501
}
 
1502
 
 
1503
void
 
1504
log_template_options_destroy(LogTemplateOptions *options)
 
1505
{
 
1506
  gint i;
 
1507
 
 
1508
  for (i = 0; i < LTZ_MAX; i++)
 
1509
    {
 
1510
      if (options->time_zone[i])
 
1511
        g_free(options->time_zone[i]);
 
1512
      if (options->time_zone_info[i])
 
1513
        time_zone_info_free(options->time_zone_info[i]);
 
1514
    }
 
1515
  options->initialized = FALSE;
 
1516
}
 
1517
 
 
1518
void
 
1519
log_template_options_defaults(LogTemplateOptions *options)
 
1520
{
 
1521
  memset(options, 0, sizeof(LogTemplateOptions));
 
1522
  options->frac_digits = -1;
 
1523
  options->ts_format = -1;
 
1524
  options->on_error = -1;
 
1525
}
 
1526
 
 
1527
GQuark
 
1528
log_template_error_quark()
 
1529
{
 
1530
  return g_quark_from_static_string("log-template-error-quark");
 
1531
}
 
1532
 
 
1533
void
 
1534
log_template_global_init(void)
 
1535
{
 
1536
  gint i;
 
1537
 
 
1538
  /* init the uptime (SYSUPTIME macro) */
 
1539
  g_get_current_time(&app_uptime);
 
1540
 
 
1541
  macro_hash = g_hash_table_new(g_str_hash, g_str_equal);
 
1542
  for (i = 0; macros[i].name; i++)
 
1543
    {
 
1544
      g_hash_table_insert(macro_hash, macros[i].name,
 
1545
                          GINT_TO_POINTER(macros[i].id));
 
1546
    }
 
1547
  return;
 
1548
}
 
1549
 
 
1550
void
 
1551
log_template_global_deinit(void)
 
1552
{
 
1553
  g_hash_table_destroy(macro_hash);
 
1554
  macro_hash = NULL;
 
1555
}
 
1556
 
 
1557
gboolean
 
1558
log_template_on_error_parse(const gchar *strictness, gint *out)
 
1559
{
 
1560
  const gchar *p = strictness;
 
1561
  gboolean silently = FALSE;
 
1562
 
 
1563
  if (!strictness)
 
1564
    {
 
1565
      *out = ON_ERROR_DROP_MESSAGE;
 
1566
      return TRUE;
 
1567
    }
 
1568
 
 
1569
  if (strncmp(strictness, "silently-", strlen("silently-")) == 0)
 
1570
    {
 
1571
      silently = TRUE;
 
1572
      p = strictness + strlen("silently-");
 
1573
    }
 
1574
 
 
1575
  if (strcmp(p, "drop-message") == 0)
 
1576
    *out = ON_ERROR_DROP_MESSAGE;
 
1577
  else if (strcmp(p, "drop-property") == 0)
 
1578
    *out = ON_ERROR_DROP_PROPERTY;
 
1579
  else if (strcmp(p, "fallback-to-string") == 0)
 
1580
    *out = ON_ERROR_FALLBACK_TO_STRING;
 
1581
  else
 
1582
    return FALSE;
 
1583
 
 
1584
  if (silently)
 
1585
    *out |= ON_ERROR_SILENT;
 
1586
 
 
1587
  return TRUE;
 
1588
}
 
1589
 
 
1590
void
 
1591
log_template_options_set_on_error(LogTemplateOptions *options, gint on_error)
 
1592
{
 
1593
  options->on_error = on_error;
 
1594
}