1 /*
2  * Copyright (c) 2011-2015 Balabit
3  * Copyright (c) 2011-2014 Gergely Nagy <algernon@balabit.hu>
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 "value-pairs/internals.h"
26 #include "value-pairs/value-pairs.h"
27 #include "logmsg/logmsg.h"
28 #include "template/templates.h"
29 #include "template/macros.h"
30 #include "type-hinting.h"
31 #include "cfg-parser.h"
32 #include "string-list.h"
33 #include "scratch-buffers.h"
34 #include "cfg.h"
35 
36 #include <ctype.h>
37 
38 typedef struct
39 {
40   GPatternSpec *pattern;
41   gboolean include;
42 } VPPatternSpec;
43 
44 typedef struct
45 {
46   gchar *name;
47   LogTemplate *template;
48 } VPPairConf;
49 
50 typedef struct
51 {
52   /* we don't own any of the fields here, it is assumed that allocations are
53    * managed by the caller */
54 
55   GString *name;
56   GString *value;
57   TypeHint type_hint;
58 } VPResultValue;
59 
60 typedef struct
61 {
62   GTree *result_tree;
63 
64   /* array of VPResultValue instances */
65   GArray *values;
66 } VPResults;
67 
68 
69 typedef enum
70 {
71   VPS_NV_PAIRS        = 0x01,
72   VPS_DOT_NV_PAIRS    = 0x02,
73   VPS_RFC3164         = 0x04,
74   VPS_RFC5424         = 0x08,
75   VPS_ALL_MACROS      = 0x10,
76   VPS_SELECTED_MACROS = 0x20,
77   VPS_SDATA           = 0x40,
78   VPS_EVERYTHING      = 0x7f,
79 } ValuePairScope;
80 
81 enum
82 {
83   VPT_MACRO,
84   VPT_NVPAIR,
85 };
86 
87 typedef struct
88 {
89   const gchar *name;
90   const gchar *alt_name;
91   gint type;
92   gint id;
93 } ValuePairSpec;
94 
95 static ValuePairSpec rfc3164[] =
96 {
97   /* there's one macro named DATE that'll be expanded specially */
98   { "FACILITY" },
99   { "PRIORITY" },
100   { "HOST"     },
101   { "PROGRAM"  },
102   { "PID"      },
103   { "MESSAGE"  },
104   { "DATE" },
105   { 0 },
106 };
107 
108 static ValuePairSpec rfc5424[] =
109 {
110   { "MSGID", },
111   { 0 },
112 };
113 
114 static ValuePairSpec selected_macros[] =
115 {
116   { "TAGS" },
117   { "SOURCEIP" },
118   { "SEQNUM" },
119   { 0 },
120 };
121 
122 static ValuePairSpec *all_macros;
123 
124 static CfgFlagHandler value_pair_scope[] =
125 {
126   { "nv-pairs",           CFH_SET, offsetof(ValuePairs, scopes), VPS_NV_PAIRS },
127   { "dot-nv-pairs",       CFH_SET, offsetof(ValuePairs, scopes), VPS_DOT_NV_PAIRS},
128   { "all-nv-pairs",       CFH_SET, offsetof(ValuePairs, scopes), VPS_NV_PAIRS | VPS_DOT_NV_PAIRS },
129   { "rfc3164",            CFH_SET, offsetof(ValuePairs, scopes), VPS_RFC3164 },
130   { "core",               CFH_SET, offsetof(ValuePairs, scopes), VPS_RFC3164 },
131   { "base",               CFH_SET, offsetof(ValuePairs, scopes), VPS_RFC3164 },
132   { "rfc5424",            CFH_SET, offsetof(ValuePairs, scopes), VPS_RFC5424 },
133   { "syslog-proto",       CFH_SET, offsetof(ValuePairs, scopes), VPS_RFC5424 },
134   { "all-macros",         CFH_SET, offsetof(ValuePairs, scopes), VPS_ALL_MACROS },
135   { "selected-macros",    CFH_SET, offsetof(ValuePairs, scopes), VPS_SELECTED_MACROS },
136   { "sdata",              CFH_SET, offsetof(ValuePairs, scopes), VPS_SDATA },
137   { "everything",         CFH_SET, offsetof(ValuePairs, scopes), VPS_EVERYTHING },
138   { NULL,                 0,       0,                            0},
139 };
140 
141 
142 static gboolean
vp_pattern_spec_eval(VPPatternSpec * self,const gchar * input)143 vp_pattern_spec_eval(VPPatternSpec *self, const gchar *input)
144 {
145   return g_pattern_match_string(self->pattern, input);
146 }
147 
148 static void
vp_pattern_spec_free(VPPatternSpec * self)149 vp_pattern_spec_free(VPPatternSpec *self)
150 {
151   g_pattern_spec_free(self->pattern);
152   g_free(self);
153 }
154 
155 static VPPatternSpec *
vp_pattern_spec_new(const gchar * pattern,gboolean include)156 vp_pattern_spec_new(const gchar *pattern, gboolean include)
157 {
158   VPPatternSpec *self = g_new0(VPPatternSpec, 1);
159 
160   self->pattern = g_pattern_spec_new(pattern);
161   self->include = include;
162   return self;
163 }
164 
165 static VPPairConf *
vp_pair_conf_new(const gchar * key,LogTemplate * value)166 vp_pair_conf_new(const gchar *key, LogTemplate *value)
167 {
168   VPPairConf *p = g_new(VPPairConf, 1);
169 
170   p->name = g_strdup(key);
171   p->template = log_template_ref(value);
172   return p;
173 }
174 
175 static void
vp_pair_conf_free(VPPairConf * vpc)176 vp_pair_conf_free(VPPairConf *vpc)
177 {
178   log_template_unref(vpc->template);
179   g_free(vpc->name);
180   g_free(vpc);
181 }
182 
183 static void
vp_result_value_init(VPResultValue * rv,GString * name,TypeHint type_hint,GString * value)184 vp_result_value_init(VPResultValue *rv, GString *name, TypeHint type_hint, GString *value)
185 {
186   rv->type_hint = type_hint;
187   rv->name = name;
188   rv->value = value;
189 }
190 
191 static void
vp_results_init(VPResults * results,GCompareFunc compare_func)192 vp_results_init(VPResults *results, GCompareFunc compare_func)
193 {
194   results->values = g_array_sized_new(FALSE, FALSE, sizeof(VPResultValue), 16);
195   results->result_tree = g_tree_new_full((GCompareDataFunc) compare_func, NULL,
196                                          NULL, NULL);
197 }
198 
199 static void
vp_results_deinit(VPResults * results)200 vp_results_deinit(VPResults *results)
201 {
202   g_tree_destroy(results->result_tree);
203   g_array_free(results->values, TRUE);
204 }
205 
206 static void
vp_results_insert(VPResults * results,GString * name,TypeHint type_hint,GString * value)207 vp_results_insert(VPResults *results, GString *name, TypeHint type_hint, GString *value)
208 {
209   VPResultValue *rv;
210   gint ndx = results->values->len;
211 
212   g_array_set_size(results->values, ndx + 1);
213   rv = &g_array_index(results->values, VPResultValue, ndx);
214   vp_result_value_init(rv, name, type_hint, value);
215   /* GTree takes over ownership of name */
216   g_tree_insert(results->result_tree, name->str, GINT_TO_POINTER(ndx));
217 }
218 
219 static GString *
vp_transform_apply(ValuePairs * vp,const gchar * key)220 vp_transform_apply (ValuePairs *vp, const gchar *key)
221 {
222   gint i;
223   GString *result = scratch_buffers_alloc();
224 
225   g_string_assign(result, key);
226 
227   if (vp->transforms->len == 0)
228     return result;
229 
230   for (i = 0; i < vp->transforms->len; i++)
231     {
232       ValuePairsTransformSet *t = (ValuePairsTransformSet *) g_ptr_array_index(vp->transforms, i);
233 
234       value_pairs_transform_set_apply(t, result);
235     }
236 
237   return result;
238 }
239 
240 /* runs over the name-value pairs requested by the user (e.g. with value_pairs_add_pair) */
241 static void
vp_pairs_foreach(gpointer data,gpointer user_data)242 vp_pairs_foreach(gpointer data, gpointer user_data)
243 {
244   ValuePairs *vp = ((gpointer *)user_data)[0];
245   LogMessage *msg = ((gpointer *)user_data)[2];
246   LogTemplateEvalOptions *options = ((gpointer *)user_data)[3];
247   VPResults *results = ((gpointer *)user_data)[5];
248   GString *sb = scratch_buffers_alloc();
249   VPPairConf *vpc = (VPPairConf *)data;
250 
251   log_template_append_format((LogTemplate *)vpc->template, msg, options, sb);
252 
253   if (vp->omit_empty_values && sb->len == 0)
254     return;
255   vp_results_insert(results, vp_transform_apply(vp, vpc->name), vpc->template->type_hint, sb);
256 }
257 
258 /* runs over the LogMessage nv-pairs, and inserts them unless excluded */
259 static gboolean
vp_msg_nvpairs_foreach(NVHandle handle,gchar * name,const gchar * value,gssize value_len,gpointer user_data)260 vp_msg_nvpairs_foreach(NVHandle handle, gchar *name,
261                        const gchar *value, gssize value_len,
262                        gpointer user_data)
263 {
264   ValuePairs *vp = ((gpointer *)user_data)[0];
265   VPResults *results = ((gpointer *)user_data)[5];
266   guint j;
267   gboolean inc;
268   GString *sb;
269 
270   if (vp->omit_empty_values && value_len == 0)
271     return FALSE;
272 
273   inc = (name[0] == '.' && (vp->scopes & VPS_DOT_NV_PAIRS)) ||
274         (name[0] != '.' && (vp->scopes & VPS_NV_PAIRS)) ||
275         (log_msg_is_handle_sdata(handle) && (vp->scopes & (VPS_SDATA + VPS_RFC5424)));
276 
277   for (j = 0; j < vp->patterns->len; j++)
278     {
279       VPPatternSpec *vps = (VPPatternSpec *) g_ptr_array_index(vp->patterns, j);
280       if (vp_pattern_spec_eval(vps, name))
281         inc = vps->include;
282     }
283 
284   if (!inc)
285     return FALSE;
286 
287   sb = scratch_buffers_alloc();
288 
289   g_string_append_len(sb, value, value_len);
290   vp_results_insert(results, vp_transform_apply(vp, name), TYPE_HINT_STRING, sb);
291 
292   return FALSE;
293 }
294 
295 static gboolean
vp_find_in_set(ValuePairs * vp,const gchar * name,gboolean exclude)296 vp_find_in_set(ValuePairs *vp, const gchar *name, gboolean exclude)
297 {
298   guint j;
299   gboolean included = exclude;
300 
301   for (j = 0; j < vp->patterns->len; j++)
302     {
303       VPPatternSpec *vps = (VPPatternSpec *) g_ptr_array_index(vp->patterns, j);
304 
305       if (vp_pattern_spec_eval(vps, name))
306         included = vps->include;
307     }
308 
309   return included;
310 }
311 
312 static void
vp_merge_other_set(ValuePairs * vp,ValuePairSpec * set,gboolean exclude)313 vp_merge_other_set(ValuePairs *vp, ValuePairSpec *set, gboolean exclude)
314 {
315   gint i;
316 
317   for (i = 0; set[i].name; i++)
318     {
319       if (!vp_find_in_set(vp, set[i].name, exclude))
320         continue;
321 
322       g_ptr_array_add(vp->builtins, &set[i]);
323     }
324 }
325 
326 /* runs over the all macros and merges the selected ones by the pattern into the value-pair set */
327 static void
vp_merge_macros(ValuePairs * vp)328 vp_merge_macros(ValuePairs *vp)
329 {
330   vp_merge_other_set(vp, all_macros, FALSE);
331 }
332 
333 /* runs over a set of ValuePairSpec structs and merges them into the value-pair set */
334 static void
vp_merge_set(ValuePairs * vp,ValuePairSpec * set)335 vp_merge_set(ValuePairs *vp, ValuePairSpec *set)
336 {
337   vp_merge_other_set(vp, set, TRUE);
338 }
339 
340 
341 static void
vp_update_builtin_list_of_values(ValuePairs * vp)342 vp_update_builtin_list_of_values(ValuePairs *vp)
343 {
344   g_ptr_array_set_size(vp->builtins, 0);
345 
346   if (vp->patterns->len > 0)
347     vp_merge_macros(vp);
348 
349   if (vp->scopes & (VPS_RFC3164 + VPS_RFC5424 + VPS_SELECTED_MACROS))
350     vp_merge_set(vp, rfc3164);
351 
352   if (vp->scopes & VPS_RFC5424)
353     vp_merge_set(vp, rfc5424);
354 
355   if (vp->scopes & VPS_SELECTED_MACROS)
356     vp_merge_set(vp, selected_macros);
357 
358   if (vp->scopes & VPS_ALL_MACROS)
359     vp_merge_set(vp, all_macros);
360 }
361 
362 static void
vp_merge_builtins(ValuePairs * vp,VPResults * results,LogMessage * msg,LogTemplateEvalOptions * options)363 vp_merge_builtins(ValuePairs *vp, VPResults *results, LogMessage *msg, LogTemplateEvalOptions *options)
364 {
365   gint i;
366   GString *sb;
367 
368   for (i = 0; i < vp->builtins->len; i++)
369     {
370       ValuePairSpec *spec = (ValuePairSpec *) g_ptr_array_index(vp->builtins, i);
371 
372       sb = scratch_buffers_alloc();
373 
374       switch (spec->type)
375         {
376         case VPT_MACRO:
377           log_macro_expand(sb, spec->id, FALSE, options, msg);
378           break;
379         case VPT_NVPAIR:
380         {
381           const gchar *nv;
382           gssize len;
383 
384           nv = log_msg_get_value(msg, (NVHandle) spec->id, &len);
385           g_string_append_len(sb, nv, len);
386           break;
387         }
388         default:
389           g_assert_not_reached();
390         }
391 
392       if (sb->len == 0)
393         {
394           continue;
395         }
396 
397       vp_results_insert(results, vp_transform_apply(vp, spec->name), TYPE_HINT_STRING, sb);
398     }
399 }
400 
401 static gboolean
vp_foreach_helper(const gchar * name,gpointer ndx_as_pointer,gpointer data)402 vp_foreach_helper(const gchar *name, gpointer ndx_as_pointer, gpointer data)
403 {
404   VPResults *results = ((gpointer *)data)[0];
405   gint ndx = GPOINTER_TO_INT(ndx_as_pointer);
406   VPResultValue *rv = &g_array_index(results->values, VPResultValue, ndx);
407   VPForeachFunc func = ((gpointer *)data)[1];
408   gpointer user_data = ((gpointer *)data)[2];
409   gboolean *r = ((gpointer *)data)[3];
410 
411   *r &= !func(name, rv->type_hint,
412               rv->value->str,
413               rv->value->len, user_data);
414   return !*r;
415 }
416 
417 
418 gboolean
value_pairs_foreach_sorted(ValuePairs * vp,VPForeachFunc func,GCompareFunc compare_func,LogMessage * msg,LogTemplateEvalOptions * options,gpointer user_data)419 value_pairs_foreach_sorted (ValuePairs *vp, VPForeachFunc func,
420                             GCompareFunc compare_func,
421                             LogMessage *msg, LogTemplateEvalOptions *options,
422                             gpointer user_data)
423 {
424   gpointer args[] = { vp, func, msg, options, user_data, NULL};
425   gboolean result = TRUE;
426   VPResults results;
427   gpointer helper_args[] = { &results, func, user_data, &result };
428   ScratchBuffersMarker mark;
429 
430   scratch_buffers_mark(&mark);
431   vp_results_init(&results, compare_func);
432   args[5] = &results;
433 
434   /*
435    * Build up the base set
436    */
437   if (vp->scopes & (VPS_NV_PAIRS + VPS_DOT_NV_PAIRS + VPS_SDATA + VPS_RFC5424) ||
438       vp->patterns->len > 0)
439     nv_table_foreach(msg->payload, logmsg_registry,
440                      (NVTableForeachFunc) vp_msg_nvpairs_foreach, args);
441 
442   vp_merge_builtins(vp, &results, msg, options);
443 
444   /* Merge the explicit key-value pairs too */
445   g_ptr_array_foreach(vp->vpairs, (GFunc)vp_pairs_foreach, args);
446 
447   /* Aaand we run it through the callback! */
448   g_tree_foreach(results.result_tree, (GTraverseFunc)vp_foreach_helper, helper_args);
449   vp_results_deinit(&results);
450   scratch_buffers_reclaim_marked(mark);
451 
452   return result;
453 }
454 
455 gboolean
value_pairs_foreach(ValuePairs * vp,VPForeachFunc func,LogMessage * msg,LogTemplateEvalOptions * options,gpointer user_data)456 value_pairs_foreach(ValuePairs *vp, VPForeachFunc func,
457                     LogMessage *msg, LogTemplateEvalOptions *options,
458                     gpointer user_data)
459 {
460   return value_pairs_foreach_sorted(vp, func, (GCompareFunc) strcmp,
461                                     msg, options, user_data);
462 }
463 
464 /*******************************************************************************
465  * vp_stack (represented by vp_stack_t)
466  *
467  * A not very generic stack implementation used by vp_walker.
468  *******************************************************************************/
469 
470 #define VP_STACK_INITIAL_SIZE 16
471 
472 typedef struct
473 {
474   GPtrArray *elems;
475 } vp_stack_t;
476 
477 static void
vp_stack_init(vp_stack_t * stack)478 vp_stack_init(vp_stack_t *stack)
479 {
480   stack->elems = g_ptr_array_sized_new(VP_STACK_INITIAL_SIZE);
481 }
482 
483 static void
vp_stack_destroy(vp_stack_t * stack)484 vp_stack_destroy(vp_stack_t *stack)
485 {
486   g_ptr_array_free(stack->elems, TRUE);
487 }
488 
489 static void
vp_stack_push(vp_stack_t * stack,gpointer data)490 vp_stack_push(vp_stack_t *stack, gpointer data)
491 {
492   g_ptr_array_add(stack->elems, data);
493 }
494 
495 static gpointer
vp_stack_peek(vp_stack_t * stack)496 vp_stack_peek(vp_stack_t *stack)
497 {
498   if (stack->elems->len == 0)
499     return NULL;
500 
501   return g_ptr_array_index(stack->elems, stack->elems->len - 1);
502 }
503 
504 static gpointer
vp_stack_pop(vp_stack_t * stack)505 vp_stack_pop(vp_stack_t *stack)
506 {
507   gpointer data = NULL;
508 
509   data = vp_stack_peek(stack);
510   if (data)
511     g_ptr_array_remove_index(stack->elems, stack->elems->len - 1);
512   return data;
513 }
514 
515 static guint
vp_stack_height(vp_stack_t * stack)516 vp_stack_height(vp_stack_t *stack)
517 {
518   return stack->elems->len;
519 }
520 
521 /*******************************************************************************
522  * vp_walker (represented by vp_walk_state_t),
523  *
524  * The stuff that translates name-value pairs to a tree with SAX like
525  * callbacks. (start/value/end)
526  *******************************************************************************/
527 
528 typedef struct
529 {
530   gchar *key;
531   gchar *prefix;
532   gint prefix_len;
533 
534   gpointer data;
535 } vp_walk_stack_data_t;
536 
537 typedef struct
538 {
539   VPWalkCallbackFunc obj_start;
540   VPWalkCallbackFunc obj_end;
541   VPWalkValueCallbackFunc process_value;
542 
543   gpointer user_data;
544   vp_stack_t stack;
545 } vp_walk_state_t;
546 
547 static vp_walk_stack_data_t *
vp_walker_stack_push(vp_stack_t * stack,gchar * key,gchar * prefix)548 vp_walker_stack_push (vp_stack_t *stack,
549                       gchar *key, gchar *prefix)
550 {
551   vp_walk_stack_data_t *nt = g_new(vp_walk_stack_data_t, 1);
552 
553   nt->key = key;
554   nt->prefix = prefix;
555   nt->prefix_len = strlen(nt->prefix);
556   nt->data = NULL;
557 
558   vp_stack_push(stack, nt);
559   return nt;
560 }
561 
562 static vp_walk_stack_data_t *
vp_walker_stack_peek(vp_stack_t * stack)563 vp_walker_stack_peek(vp_stack_t *stack)
564 {
565   return (vp_walk_stack_data_t *) vp_stack_peek(stack);
566 }
567 
568 static vp_walk_stack_data_t *
vp_walker_stack_pop(vp_stack_t * stack)569 vp_walker_stack_pop(vp_stack_t *stack)
570 {
571   return (vp_walk_stack_data_t *) vp_stack_pop(stack);
572 }
573 
574 static void
vp_walker_free_stack_data(vp_walk_stack_data_t * t)575 vp_walker_free_stack_data(vp_walk_stack_data_t *t)
576 {
577   g_free(t->key);
578   g_free(t->prefix);
579   g_free(t);
580 }
581 
582 static void
vp_walker_stack_unwind_containers_until(vp_walk_state_t * state,const gchar * name)583 vp_walker_stack_unwind_containers_until(vp_walk_state_t *state,
584                                         const gchar *name)
585 {
586   vp_walk_stack_data_t *t;
587 
588   while ((t = vp_walker_stack_pop(&state->stack)) != NULL)
589     {
590       vp_walk_stack_data_t *p;
591 
592       if (name && strncmp(name, t->prefix, t->prefix_len) == 0)
593         {
594           /* This one matched, put it back, PUT IT BACK! */
595           vp_stack_push(&state->stack, t);
596           break;
597         }
598 
599       p = vp_walker_stack_peek(&state->stack);
600 
601       if (p)
602         state->obj_end(t->key, t->prefix, &t->data,
603                        p->prefix, &p->data,
604                        state->user_data);
605       else
606         state->obj_end(t->key, t->prefix, &t->data,
607                        NULL, NULL,
608                        state->user_data);
609       vp_walker_free_stack_data(t);
610     }
611 }
612 
613 static void
vp_walker_stack_unwind_all_containers(vp_walk_state_t * state)614 vp_walker_stack_unwind_all_containers(vp_walk_state_t *state)
615 {
616   vp_walker_stack_unwind_containers_until(state, NULL);
617 }
618 
619 static const gchar *
vp_walker_skip_sdata_enterprise_id(const gchar * name)620 vp_walker_skip_sdata_enterprise_id(const gchar *name)
621 {
622   /* parse .SDATA.foo@1234.56.678 format, starting with the '@'
623      character. Assume that any numbers + dots form part of the
624      "foo@1234.56.678" key, even if they contain dots */
625   do
626     {
627       /* skip @ or . */
628       ++name;
629       name += strspn(name, "0123456789");
630     }
631   while (*name == '.' && isdigit(*(name + 1)));
632   return name;
633 }
634 
635 static GPtrArray *
vp_walker_split_name_to_tokens(vp_walk_state_t * state,const gchar * name)636 vp_walker_split_name_to_tokens(vp_walk_state_t *state, const gchar *name)
637 {
638   const gchar *token_start = name;
639   const gchar *token_end = name;
640 
641   GPtrArray *array = g_ptr_array_sized_new(VP_STACK_INITIAL_SIZE);
642 
643   while (*token_end)
644     {
645       switch (*token_end)
646         {
647         case '@':
648           token_end = vp_walker_skip_sdata_enterprise_id(token_end);
649           break;
650         case '.':
651           if (token_start != token_end)
652             {
653               g_ptr_array_add(array, g_strndup(token_start, token_end - token_start));
654               ++token_end;
655               token_start = token_end;
656               break;
657             }
658         /* fall through, zero length token is not considered a separate token */
659         default:
660           ++token_end;
661           token_end += strcspn(token_end, "@.");
662           break;
663         }
664     }
665 
666   if (token_start != token_end)
667     g_ptr_array_add(array, g_strndup(token_start, token_end - token_start));
668 
669   if (array->len == 0)
670     {
671       g_ptr_array_free(array, TRUE);
672       return NULL;
673     }
674 
675   return array;
676 }
677 
678 static gchar *
vp_walker_name_combine_prefix(GPtrArray * tokens,gint until)679 vp_walker_name_combine_prefix(GPtrArray *tokens, gint until)
680 {
681   GString *s = scratch_buffers_alloc();
682   gchar *str;
683   gint i;
684 
685   for (i = 0; i < until; i++)
686     {
687       g_string_append(s, g_ptr_array_index(tokens, i));
688       g_string_append_c(s, '.');
689     }
690   g_string_append(s, g_ptr_array_index(tokens, until));
691 
692   str = g_strdup(s->str);
693   return str;
694 }
695 
696 static gchar *
vp_walker_start_containers_for_name(vp_walk_state_t * state,const gchar * name)697 vp_walker_start_containers_for_name(vp_walk_state_t *state,
698                                     const gchar *name)
699 {
700   GPtrArray *tokens;
701   gchar *key = NULL;
702   guint i, start;
703 
704   tokens = vp_walker_split_name_to_tokens(state, name);
705 
706   start = vp_stack_height(&state->stack);
707   for (i = start; i < tokens->len - 1; i++)
708     {
709       vp_walk_stack_data_t *p, *nt;
710 
711       p = vp_walker_stack_peek(&state->stack);
712       nt = vp_walker_stack_push(&state->stack,
713                                 g_strdup(g_ptr_array_index(tokens, i)),
714                                 vp_walker_name_combine_prefix(tokens, i));
715 
716       if (p)
717         state->obj_start(nt->key, nt->prefix, &nt->data,
718                          p->prefix, &p->data,
719                          state->user_data);
720       else
721         state->obj_start(nt->key, nt->prefix, &nt->data,
722                          NULL, NULL, state->user_data);
723     }
724 
725   /* The last token is the key (well, second to last, last being
726      NULL), so treat that normally. */
727   key = g_ptr_array_index(tokens, tokens->len - 1);
728   g_ptr_array_index(tokens, tokens->len - 1) = NULL;
729 
730   g_ptr_array_foreach(tokens, (GFunc)g_free, NULL);
731   g_ptr_array_free(tokens, TRUE);
732 
733   return key;
734 }
735 
736 static gboolean
value_pairs_walker(const gchar * name,TypeHint type,const gchar * value,gsize value_len,gpointer user_data)737 value_pairs_walker(const gchar *name, TypeHint type, const gchar *value, gsize value_len,
738                    gpointer user_data)
739 {
740   vp_walk_state_t *state = (vp_walk_state_t *)user_data;
741   vp_walk_stack_data_t *data;
742   gchar *key;
743   gboolean result;
744 
745   vp_walker_stack_unwind_containers_until(state, name);
746   key = vp_walker_start_containers_for_name(state, name);
747   data = vp_walker_stack_peek(&state->stack);
748 
749   if (data != NULL)
750     result = state->process_value(key, data->prefix,
751                                   type, value, value_len,
752                                   &data->data,
753                                   state->user_data);
754   else
755     result = state->process_value(key, NULL,
756                                   type, value, value_len,
757                                   NULL,
758                                   state->user_data);
759 
760   g_free(key);
761 
762   return result;
763 }
764 
765 static gint
vp_walk_cmp(const gchar * s1,const gchar * s2)766 vp_walk_cmp(const gchar *s1, const gchar *s2)
767 {
768   return strcmp(s2, s1);
769 }
770 
771 /*******************************************************************************
772  * Public API
773  *******************************************************************************/
774 
775 gboolean
value_pairs_walk(ValuePairs * vp,VPWalkCallbackFunc obj_start_func,VPWalkValueCallbackFunc process_value_func,VPWalkCallbackFunc obj_end_func,LogMessage * msg,LogTemplateEvalOptions * options,gpointer user_data)776 value_pairs_walk(ValuePairs *vp,
777                  VPWalkCallbackFunc obj_start_func,
778                  VPWalkValueCallbackFunc process_value_func,
779                  VPWalkCallbackFunc obj_end_func,
780                  LogMessage *msg, LogTemplateEvalOptions *options,
781                  gpointer user_data)
782 {
783   vp_walk_state_t state;
784   gboolean result;
785 
786   state.user_data = user_data;
787   state.obj_start = obj_start_func;
788   state.obj_end = obj_end_func;
789   state.process_value = process_value_func;
790   vp_stack_init(&state.stack);
791 
792   state.obj_start(NULL, NULL, NULL, NULL, NULL, user_data);
793   result = value_pairs_foreach_sorted(vp, value_pairs_walker,
794                                       (GCompareFunc)vp_walk_cmp, msg,
795                                       options, &state);
796   vp_walker_stack_unwind_all_containers(&state);
797   state.obj_end(NULL, NULL, NULL, NULL, NULL, user_data);
798   vp_stack_destroy(&state.stack);
799 
800   return result;
801 }
802 
803 gboolean
value_pairs_add_scope(ValuePairs * vp,const gchar * scope)804 value_pairs_add_scope(ValuePairs *vp, const gchar *scope)
805 {
806   gboolean result;
807 
808   if (strcmp(scope, "none") != 0)
809     {
810       result = cfg_process_flag(value_pair_scope, vp, scope);
811       vp_update_builtin_list_of_values(vp);
812     }
813   else
814     {
815       result = TRUE;
816       vp->scopes = 0;
817       vp_update_builtin_list_of_values(vp);
818     }
819   return result;
820 }
821 
822 void
value_pairs_add_glob_pattern(ValuePairs * vp,const gchar * pattern,gboolean include)823 value_pairs_add_glob_pattern(ValuePairs *vp, const gchar *pattern,
824                              gboolean include)
825 {
826   g_ptr_array_add(vp->patterns, vp_pattern_spec_new(pattern, include));
827   vp_update_builtin_list_of_values(vp);
828 }
829 
830 void
value_pairs_add_glob_patterns(ValuePairs * vp,GList * patterns,gboolean include)831 value_pairs_add_glob_patterns(ValuePairs *vp, GList *patterns, gboolean include)
832 {
833   GList *l = patterns;
834 
835   while (l)
836     {
837       value_pairs_add_glob_pattern(vp, (gchar *)l->data, include);
838       l = g_list_next (l);
839     }
840   string_list_free(patterns);
841   vp_update_builtin_list_of_values(vp);
842 }
843 
844 void
value_pairs_add_pair(ValuePairs * vp,const gchar * key,LogTemplate * value)845 value_pairs_add_pair(ValuePairs *vp, const gchar *key, LogTemplate *value)
846 {
847   g_ptr_array_add(vp->vpairs, vp_pair_conf_new(key, value));
848   vp_update_builtin_list_of_values(vp);
849 }
850 
851 void
value_pairs_add_transforms(ValuePairs * vp,ValuePairsTransformSet * vpts)852 value_pairs_add_transforms(ValuePairs *vp, ValuePairsTransformSet *vpts)
853 {
854   g_ptr_array_add(vp->transforms, vpts);
855   vp_update_builtin_list_of_values(vp);
856 }
857 
858 ValuePairs *
value_pairs_new(void)859 value_pairs_new(void)
860 {
861   ValuePairs *vp;
862 
863   vp = g_new0(ValuePairs, 1);
864   g_atomic_counter_set(&vp->ref_cnt, 1);
865   vp->builtins = g_ptr_array_new();
866   vp->vpairs = g_ptr_array_new();
867   vp->patterns = g_ptr_array_new();
868   vp->transforms = g_ptr_array_new();
869 
870   return vp;
871 }
872 
873 ValuePairs *
value_pairs_new_default(GlobalConfig * cfg)874 value_pairs_new_default(GlobalConfig *cfg)
875 {
876   ValuePairs *vp = value_pairs_new();
877 
878   value_pairs_add_scope(vp, "selected-macros");
879   value_pairs_add_scope(vp, "nv-pairs");
880   value_pairs_add_scope(vp, "sdata");
881   return vp;
882 }
883 
884 static void
value_pairs_free(ValuePairs * vp)885 value_pairs_free (ValuePairs *vp)
886 {
887   guint i;
888 
889   for (i = 0; i < vp->vpairs->len; i++)
890     vp_pair_conf_free(g_ptr_array_index(vp->vpairs, i));
891 
892   g_ptr_array_free(vp->vpairs, TRUE);
893 
894   for (i = 0; i < vp->patterns->len; i++)
895     {
896       VPPatternSpec *vps = (VPPatternSpec *) g_ptr_array_index(vp->patterns, i);
897       vp_pattern_spec_free(vps);
898     }
899   g_ptr_array_free(vp->patterns, TRUE);
900 
901   for (i = 0; i < vp->transforms->len; i++)
902     {
903       ValuePairsTransformSet *vpts = (ValuePairsTransformSet *) g_ptr_array_index(vp->transforms, i);
904       value_pairs_transform_set_free(vpts);
905     }
906   g_ptr_array_free(vp->transforms, TRUE);
907   g_ptr_array_free(vp->builtins, TRUE);
908   g_free(vp);
909 }
910 
911 ValuePairs *
value_pairs_ref(ValuePairs * self)912 value_pairs_ref(ValuePairs *self)
913 {
914   g_assert(!self || g_atomic_counter_get(&self->ref_cnt) > 0);
915 
916   if (self)
917     {
918       g_atomic_counter_inc(&self->ref_cnt);
919     }
920   return self;
921 }
922 
923 void
value_pairs_unref(ValuePairs * self)924 value_pairs_unref(ValuePairs *self)
925 {
926   if (self)
927     {
928       g_assert(g_atomic_counter_get(&self->ref_cnt) > 0);
929 
930       if (g_atomic_counter_dec_and_test(&self->ref_cnt))
931         value_pairs_free(self);
932     }
933 }
934 
935 static void
value_pairs_init_set(ValuePairSpec * set)936 value_pairs_init_set(ValuePairSpec *set)
937 {
938   gint i;
939 
940   for (i = 0; set[i].name; i++)
941     {
942       guint id;
943       const gchar *name;
944 
945       name = set[i].alt_name ? set[i].alt_name : set[i].name;
946 
947       if ((id = log_macro_lookup(name, strlen(name))))
948         {
949           set[i].type = VPT_MACRO;
950           set[i].id = id;
951         }
952       else
953         {
954           set[i].type = VPT_NVPAIR;
955           set[i].id = log_msg_get_value_handle(name);
956         }
957     }
958 }
959 
960 void
value_pairs_global_init(void)961 value_pairs_global_init(void)
962 {
963   gint i = 0;
964   GArray *a;
965 
966   value_pairs_init_set(rfc3164);
967   value_pairs_init_set(rfc5424);
968   value_pairs_init_set(selected_macros);
969 
970   a = g_array_new(TRUE, TRUE, sizeof(ValuePairSpec));
971   for (i = 0; macros[i].name; i++)
972     {
973       ValuePairSpec pair;
974 
975       pair.name = macros[i].name;
976       pair.type = VPT_MACRO;
977       pair.id = macros[i].id;
978       g_array_append_val(a, pair);
979     }
980   all_macros = (ValuePairSpec *) g_array_free(a, FALSE);
981 }
982 
983 void
value_pairs_global_deinit(void)984 value_pairs_global_deinit(void)
985 {
986   g_free(all_macros);
987 }
988