1 /*
2  * Copyright (c) 2002-2012 Balabit
3  * Copyright (c) 1998-2012 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 "cfg-tree.h"
26 #include "logmpx.h"
27 #include "logpipe.h"
28 
29 #include <string.h>
30 
31 static void _log_expr_node_free(LogExprNode *self);
32 
33 LogExprNode *
log_expr_node_ref(LogExprNode * self)34 log_expr_node_ref(LogExprNode *self)
35 {
36   g_assert(!self || g_atomic_counter_get(&self->ref_cnt) > 0);
37 
38   if (self)
39     {
40       g_atomic_counter_inc(&self->ref_cnt);
41     }
42   return self;
43 }
44 
45 LogExprNode *
log_expr_node_unref(LogExprNode * self)46 log_expr_node_unref(LogExprNode *self)
47 {
48   g_assert(!self || g_atomic_counter_get(&self->ref_cnt));
49 
50   if (self && (g_atomic_counter_dec_and_test(&self->ref_cnt)))
51     {
52       _log_expr_node_free(self);
53       return NULL;
54     }
55   return self;
56 }
57 
58 /*
59  * Return the textual representation of a node content type.
60  */
61 const gchar *
log_expr_node_get_content_name(gint content)62 log_expr_node_get_content_name(gint content)
63 {
64   switch (content)
65     {
66     case ENC_PIPE:
67       return "log";
68     case ENC_SOURCE:
69       return "source";
70     case ENC_FILTER:
71       return "filter";
72     case ENC_PARSER:
73       return "parser";
74     case ENC_REWRITE:
75       return "rewrite";
76     case ENC_DESTINATION:
77       return "destination";
78     default:
79       g_assert_not_reached();
80       break;
81     }
82 }
83 
84 /*
85  * Return the textual representation of a node layout.
86  */
87 const gchar *
log_expr_node_get_layout_name(gint layout)88 log_expr_node_get_layout_name(gint layout)
89 {
90   switch (layout)
91     {
92     case ENL_SINGLE:
93       return "single";
94     case ENL_REFERENCE:
95       return "reference";
96     case ENL_SEQUENCE:
97       return "sequence";
98     case ENL_JUNCTION:
99       return "junction";
100     default:
101       g_assert_not_reached();
102       break;
103     }
104 }
105 
106 /* return the top-most rule that matches content. This is used to
107  * query the enclosing rule for a given LogExprNode. It is looking up
108  * the top-most node, so that we use the name of the enclosing block
109  * that the user specified. In-line defined, and thus anonymous
110  * expressions are automatically named. In that case this function
111  * will return a node matching @content but without an actual name.
112  */
113 LogExprNode *
log_expr_node_get_container_rule(LogExprNode * self,gint content)114 log_expr_node_get_container_rule(LogExprNode *self, gint content)
115 {
116   LogExprNode *node, *result = NULL;
117 
118   node = self->parent;
119   while (node)
120     {
121       if (node->content == content)
122         {
123           result = node;
124         }
125       node = node->parent;
126     }
127   return result;
128 }
129 
130 /**
131  * log_expr_node_append:
132  * @a: first LogExprNode
133  * @b: second LogExprNode
134  *
135  * This function appends @b to @a in a linked list using the ep_next field
136  * in LogExprNode.
137  **/
138 void
log_expr_node_append(LogExprNode * a,LogExprNode * b)139 log_expr_node_append(LogExprNode *a, LogExprNode *b)
140 {
141   a->next = b;
142 }
143 
144 LogExprNode *
log_expr_node_append_tail(LogExprNode * a,LogExprNode * b)145 log_expr_node_append_tail(LogExprNode *a, LogExprNode *b)
146 {
147   if (a)
148     {
149       LogExprNode *p = a;
150       while (p->next)
151         p = p->next;
152       log_expr_node_append(p, b);
153       return a;
154     }
155   return b;
156 }
157 
158 /*
159  * Format the location information for a LogExprNode. For nodes that
160  * have no location information, the parent is considered, this way
161  * always returning something close to the location that defined the
162  * node.
163  */
164 const gchar *
log_expr_node_format_location(LogExprNode * self,gchar * buf,gsize buf_len)165 log_expr_node_format_location(LogExprNode *self, gchar *buf, gsize buf_len)
166 {
167   LogExprNode *node = self;
168 
169   while (node)
170     {
171       if (node->line || node->column)
172         {
173           g_snprintf(buf, buf_len, "%s:%d:%d", self->filename ? : "#buffer", node->line, node->column);
174           break;
175         }
176       node = node->parent;
177     }
178   if (!node)
179     strncpy(buf, "#unknown", buf_len);
180   return buf;
181 }
182 
183 EVTTAG *
log_expr_node_location_tag(LogExprNode * self)184 log_expr_node_location_tag(LogExprNode *self)
185 {
186   gchar buf[128];
187 
188   return evt_tag_str("location", log_expr_node_format_location(self, buf, sizeof(buf)));
189 }
190 
191 /*
192  * Set the list of children of a LogExprNode. It automatically updates
193  * the children's "parent" pointers so that the tree can be traversed
194  * upwards too.
195  */
196 void
log_expr_node_set_children(LogExprNode * self,LogExprNode * children)197 log_expr_node_set_children(LogExprNode *self, LogExprNode *children)
198 {
199   LogExprNode *ep;
200 
201   /* we don't currently support setting the children list multiple
202    * times. no inherent reason, just the proper free function would
203    * need to be written, until then this assert would reveal the case
204    * quite fast.
205    */
206 
207   g_assert(self->children == NULL);
208 
209   for (ep = children; ep; ep = ep->next)
210     ep->parent = self;
211 
212   self->children = children;
213 }
214 
215 /*
216  * Set the object associated with a node. The "object" member is used
217  * to store the LogPipe instance associated with ENL_SINGLE/ENC_PIPE
218  * nodes.
219  */
220 void
log_expr_node_set_object(LogExprNode * self,gpointer object,GDestroyNotify destroy)221 log_expr_node_set_object(LogExprNode *self, gpointer object, GDestroyNotify destroy)
222 {
223   self->object = object;
224   self->object_destroy = destroy;
225 }
226 
227 /*
228  * The aux object is the secondary object associated with a node, it
229  * is mostly unused, except for nodes storing source and destination
230  * statements, in which case this contains a reference to the last
231  * item of the compiled sequence (for sources) or the first item of
232  * the compiled sequence (destinations).
233  *
234  * This mechanism is used to avoid having to clone source/destination
235  * pipes, which operation they don't support.
236  */
237 void
log_expr_node_set_aux(LogExprNode * self,gpointer aux,GDestroyNotify destroy)238 log_expr_node_set_aux(LogExprNode *self, gpointer aux, GDestroyNotify destroy)
239 {
240   self->aux = aux;
241   self->aux_destroy = destroy;
242 }
243 
244 /**
245  * log_expr_node_new:
246  * @layout: layout of the children (ENL_*)
247  * @content: what kind of expression this node stores (ENC_*)
248  * @name: name of this rule (optional)
249  * @children: child nodes
250  * @flags: a combination of LC_* flags as specified by the administrator
251  * @yylloc: the lexer location in the configuration file.
252  *
253  * This function constructs a LogExprNode object which encapsulates a
254  * log expression in the configuration. See the note in cfg-tree.h for
255  * more information about LogExprNode objects and log expressions.
256  **/
257 LogExprNode *
log_expr_node_new(gint layout,gint content,const gchar * name,LogExprNode * children,guint32 flags,CFG_LTYPE * yylloc)258 log_expr_node_new(gint layout, gint content, const gchar *name, LogExprNode *children, guint32 flags, CFG_LTYPE *yylloc)
259 {
260   LogExprNode *self = g_new0(LogExprNode, 1);
261 
262   g_atomic_counter_set(&self->ref_cnt, 1);
263 
264   self->layout = layout;
265   self->content = content;
266   self->name = g_strdup(name);
267   log_expr_node_set_children(self, children);
268   self->flags = flags;
269   if (yylloc)
270     {
271       self->filename = g_strdup(yylloc->level->name);
272       self->line = yylloc->first_line;
273       self->column = yylloc->first_column;
274     }
275   return self;
276 }
277 
278 /**
279  * log_expr_node_free:
280  * @self: LogExprNode instance
281  *
282  * This function frees the LogExprNode object encapsulating a log
283  * expression node pointed to by @self.
284  **/
285 static void
_log_expr_node_free(LogExprNode * self)286 _log_expr_node_free(LogExprNode *self)
287 {
288   LogExprNode *next, *p;
289 
290   for (p = self->children ; p; p = next)
291     {
292       next = p->next;
293       log_expr_node_unref(p);
294     }
295   if (self->object && self->object_destroy)
296     self->object_destroy(self->object);
297   if (self->aux && self->aux_destroy)
298     self->aux_destroy(self->aux);
299   g_free(self->name);
300   g_free(self->filename);
301   g_free(self);
302 }
303 
304 LogExprNode *
log_expr_node_new_pipe(LogPipe * pipe,CFG_LTYPE * yylloc)305 log_expr_node_new_pipe(LogPipe *pipe, CFG_LTYPE *yylloc)
306 {
307   LogExprNode *node = log_expr_node_new(ENL_SINGLE, ENC_PIPE, NULL, NULL, 0, yylloc);
308 
309   log_expr_node_set_object(node, pipe, (GDestroyNotify) log_pipe_unref);
310   return node;
311 }
312 
313 LogExprNode *
log_expr_node_new_source(const gchar * name,LogExprNode * children,CFG_LTYPE * yylloc)314 log_expr_node_new_source(const gchar *name, LogExprNode *children, CFG_LTYPE *yylloc)
315 {
316   return log_expr_node_new(ENL_SEQUENCE, ENC_SOURCE, name, children, 0, yylloc);
317 }
318 
319 LogExprNode *
log_expr_node_new_source_reference(const gchar * name,CFG_LTYPE * yylloc)320 log_expr_node_new_source_reference(const gchar *name, CFG_LTYPE *yylloc)
321 {
322   return log_expr_node_new(ENL_REFERENCE, ENC_SOURCE, name, NULL, 0, yylloc);
323 }
324 
325 LogExprNode *
log_expr_node_new_destination(const gchar * name,LogExprNode * children,CFG_LTYPE * yylloc)326 log_expr_node_new_destination(const gchar *name, LogExprNode *children, CFG_LTYPE *yylloc)
327 {
328   return log_expr_node_new(ENL_SEQUENCE, ENC_DESTINATION, name, children, 0, yylloc);
329 }
330 
331 LogExprNode *
log_expr_node_new_destination_reference(const gchar * name,CFG_LTYPE * yylloc)332 log_expr_node_new_destination_reference(const gchar *name, CFG_LTYPE *yylloc)
333 {
334   return log_expr_node_new(ENL_REFERENCE, ENC_DESTINATION, name, NULL, 0, yylloc);
335 }
336 
337 LogExprNode *
log_expr_node_new_filter(const gchar * name,LogExprNode * child,CFG_LTYPE * yylloc)338 log_expr_node_new_filter(const gchar *name, LogExprNode *child, CFG_LTYPE *yylloc)
339 {
340   return log_expr_node_new(ENL_SEQUENCE, ENC_FILTER, name, child, 0, yylloc);
341 }
342 
343 LogExprNode *
log_expr_node_new_filter_reference(const gchar * name,CFG_LTYPE * yylloc)344 log_expr_node_new_filter_reference(const gchar *name, CFG_LTYPE *yylloc)
345 {
346   return log_expr_node_new(ENL_REFERENCE, ENC_FILTER, name, NULL, 0, yylloc);
347 }
348 
349 LogExprNode *
log_expr_node_new_parser(const gchar * name,LogExprNode * children,CFG_LTYPE * yylloc)350 log_expr_node_new_parser(const gchar *name, LogExprNode *children, CFG_LTYPE *yylloc)
351 {
352   return log_expr_node_new(ENL_SEQUENCE, ENC_PARSER, name, children, 0, yylloc);
353 }
354 
355 LogExprNode *
log_expr_node_new_parser_reference(const gchar * name,CFG_LTYPE * yylloc)356 log_expr_node_new_parser_reference(const gchar *name, CFG_LTYPE *yylloc)
357 {
358   return log_expr_node_new(ENL_REFERENCE, ENC_PARSER, name, NULL, 0, yylloc);
359 }
360 
361 LogExprNode *
log_expr_node_new_rewrite(const gchar * name,LogExprNode * children,CFG_LTYPE * yylloc)362 log_expr_node_new_rewrite(const gchar *name, LogExprNode *children, CFG_LTYPE *yylloc)
363 {
364   return log_expr_node_new(ENL_SEQUENCE, ENC_REWRITE, name, children, 0, yylloc);
365 }
366 
367 LogExprNode *
log_expr_node_new_rewrite_reference(const gchar * name,CFG_LTYPE * yylloc)368 log_expr_node_new_rewrite_reference(const gchar *name, CFG_LTYPE *yylloc)
369 {
370   return log_expr_node_new(ENL_REFERENCE, ENC_REWRITE, name, NULL, 0, yylloc);
371 }
372 
373 LogExprNode *
log_expr_node_new_log(LogExprNode * children,guint32 flags,CFG_LTYPE * yylloc)374 log_expr_node_new_log(LogExprNode *children, guint32 flags, CFG_LTYPE *yylloc)
375 {
376   return log_expr_node_new(ENL_SEQUENCE, ENC_PIPE, NULL, children, flags, yylloc);
377 }
378 
379 LogExprNode *
log_expr_node_new_sequence(LogExprNode * children,CFG_LTYPE * yylloc)380 log_expr_node_new_sequence(LogExprNode *children, CFG_LTYPE *yylloc)
381 {
382   return log_expr_node_new(ENL_SEQUENCE, ENC_PIPE, NULL, children, 0, yylloc);
383 }
384 
385 LogExprNode *
log_expr_node_new_junction(LogExprNode * children,CFG_LTYPE * yylloc)386 log_expr_node_new_junction(LogExprNode *children, CFG_LTYPE *yylloc)
387 {
388   return log_expr_node_new(ENL_JUNCTION, ENC_PIPE, NULL, children, 0, yylloc);
389 }
390 
391 /****************************************************************************
392  * Functions related to conditional nodes
393  *
394  * These are higher-level functions that map if-elif-else structure to
395  * LogExprNode instances.  Rather than using a higher level data struct
396  * which then generates LogExprNode instances, we represent/manipulate the
397  * if-elif-else structure right within LogExprNode.
398  *
399  * A conditional node is simply a junction with two children:
400  *
401  *     1) the first child is the "TRUE" branch, with the filter expression
402  *     attached and the final flag
403  *
404  *     2) the second child is the "FALSE" branch, possibly empty, but also
405  *     the final flag.
406  *
407  * Basically this is the equivalent to:
408  *
409  *     junction {
410  *       channel {
411  *         filter { EXPRESSION; };
412  *         flags(final);
413  *       };
414  *       channel {
415  *         flags(final);
416  *       };
417  *     };
418  *
419  * When parsing an if block, we generate both children immediately, with the
420  * empty 2nd channel, and then if an elif or else comes, the FALSE branch
421  * gets replaced.
422  *
423  * The series of if-elif-else sequences is represented by its first
424  * LogExprNode (e.g.  the first if).  When we need to add an else or elif,
425  * we would have to locate the last dangling if statement based on this
426  * first LogExprNode.  The alternative would have been to store the last if
427  * statement in a variable, however that becomes pretty complicated if we
428  * need to handle nesting.
429  *
430  ****************************************************************************/
431 
432 static LogExprNode *
log_expr_node_conditional_get_true_branch(LogExprNode * node)433 log_expr_node_conditional_get_true_branch(LogExprNode *node)
434 {
435   g_assert(node->layout == ENL_JUNCTION);
436 
437   LogExprNode *branches = node->children;
438 
439   g_assert(branches != NULL);
440   g_assert(branches->next != NULL);
441   g_assert(branches->next->next == NULL);
442 
443   /* first child */
444   return branches;
445 }
446 
447 static LogExprNode *
log_expr_node_conditional_get_false_branch(LogExprNode * node)448 log_expr_node_conditional_get_false_branch(LogExprNode *node)
449 {
450   g_assert(node->layout == ENL_JUNCTION);
451 
452   LogExprNode *branches = node->children;
453   g_assert(branches != NULL);
454   g_assert(branches->next != NULL);
455   g_assert(branches->next->next == NULL);
456 
457   /* second child */
458   return branches->next;
459 }
460 
461 static gboolean
log_expr_node_conditional_is_branch_empty(LogExprNode * node)462 log_expr_node_conditional_is_branch_empty(LogExprNode *node)
463 {
464   return node->children == NULL;
465 }
466 
467 /* this function locates the last dangling if, based on the very first if
468  * statement in a series of ifs at the same level */
469 static LogExprNode *
_locate_last_conditional_along_nested_else_blocks(LogExprNode * head)470 _locate_last_conditional_along_nested_else_blocks(LogExprNode *head)
471 {
472   while (1)
473     {
474       g_assert(log_expr_node_conditional_get_true_branch(head) != NULL);
475       g_assert(log_expr_node_conditional_get_false_branch(head) != NULL);
476 
477       LogExprNode *false_branch = log_expr_node_conditional_get_false_branch(head);
478 
479       /* check if this is the last else */
480       if (log_expr_node_conditional_is_branch_empty(false_branch))
481         return head;
482       head = false_branch->children;
483     }
484   g_assert_not_reached();
485 }
486 
487 /* change the FALSE branch (e.g. the else case) of the last dangling if, specified by the head element */
488 void
log_expr_node_conditional_set_false_branch_of_the_last_if(LogExprNode * conditional_head_node,LogExprNode * false_expr)489 log_expr_node_conditional_set_false_branch_of_the_last_if(LogExprNode *conditional_head_node, LogExprNode *false_expr)
490 {
491   LogExprNode *conditional_node = _locate_last_conditional_along_nested_else_blocks(conditional_head_node);
492   LogExprNode *branches = conditional_node->children;
493 
494   /* a conditional branch always have two children (see the constructor
495    * below), the first one is the "true" branch and the second one is the
496    * "false" branch, as they are constructed as final log channels with
497    * filter statement in the first one as the "if" expression.  */
498 
499   /* assert that we only have two children */
500   g_assert(branches != NULL);
501   g_assert(branches->next != NULL);
502   g_assert(branches->next->next == NULL);
503 
504 
505   /* construct the new false branch */
506   LogExprNode *false_branch = log_expr_node_new_log(
507                                 false_expr,
508                                 log_expr_node_lookup_flag("final"),
509                                 NULL
510                               );
511 
512   /* unlink and free the old one */
513   LogExprNode *old_false_branch = branches->next;
514   branches->next = false_branch;
515   false_branch->parent = conditional_node;
516   log_expr_node_unref(old_false_branch);
517 }
518 
519 /*
520  */
521 LogExprNode *
log_expr_node_new_conditional_with_filter(LogExprNode * filter_pipe,LogExprNode * true_expr,CFG_LTYPE * yylloc)522 log_expr_node_new_conditional_with_filter(LogExprNode *filter_pipe, LogExprNode *true_expr, CFG_LTYPE *yylloc)
523 {
524   LogExprNode *filter_node = log_expr_node_new_filter(NULL, filter_pipe, NULL);
525 
526   /*
527    *  channel {
528    *    filter { EXPRESSION };
529    *    true_expr;
530    *    flags(final);
531    *  };
532    */
533   LogExprNode *true_branch = log_expr_node_new_log(
534                                log_expr_node_append_tail(
535                                  filter_node,
536                                  log_expr_node_new_log(true_expr, LC_DROP_UNMATCHED, NULL)
537                                ),
538                                LC_FINAL,
539                                NULL
540                              );
541 
542   /*
543    *  channel {
544    *    flags(final);
545    *  };
546    *
547    * NOTE: the false branch may be modified later, once an else or elif is
548    * encountered in the configuration, see
549    * log_expr_node_conditional_set_false_branch_of_the_last_if() function
550    * above.
551    */
552   LogExprNode *false_branch = log_expr_node_new_log(
553                                 NULL,
554                                 LC_FINAL,
555                                 NULL
556                               );
557   return log_expr_node_new_junction(
558            log_expr_node_append_tail(true_branch, false_branch),
559            yylloc
560          );
561 }
562 
563 LogExprNode *
log_expr_node_new_conditional_with_block(LogExprNode * block,CFG_LTYPE * yylloc)564 log_expr_node_new_conditional_with_block(LogExprNode *block, CFG_LTYPE *yylloc)
565 {
566   /*
567    *  channel {
568    *    filtering_and_parsing_expr;
569    *    flags(final);
570    *  };
571    */
572   LogExprNode *true_branch = log_expr_node_new_log(
573                                block,
574                                LC_FINAL,
575                                NULL
576                              );
577 
578   /*
579    *  channel {
580    *    flags(final);
581    *  };
582    *
583    * NOTE: the false branch may be modified later, once an else or elif is
584    * encountered in the configuration, see
585    * log_expr_node_conditional_set_false_branch_of_the_last_if() function
586    * above.
587    */
588   LogExprNode *false_branch = log_expr_node_new_log(
589                                 NULL,
590                                 LC_FINAL,
591                                 NULL
592                               );
593   return log_expr_node_new_junction(
594            log_expr_node_append_tail(true_branch, false_branch),
595            yylloc
596          );
597 }
598 
599 /****************************************************************************/
600 
601 gint
log_expr_node_lookup_flag(const gchar * flag)602 log_expr_node_lookup_flag(const gchar *flag)
603 {
604   if (strcmp(flag, "catch-all") == 0 || strcmp(flag, "catchall") == 0)
605     return LC_CATCHALL;
606   else if (strcmp(flag, "fallback") == 0)
607     return LC_FALLBACK;
608   else if (strcmp(flag, "final") == 0)
609     return LC_FINAL;
610   else if (strcmp(flag, "flow-control") == 0)
611     return LC_FLOW_CONTROL;
612   else if (strcmp(flag, "drop-unmatched") == 0)
613     return LC_DROP_UNMATCHED;
614   msg_error("Unknown log statement flag", evt_tag_str("flag", flag));
615   return 0;
616 }
617 
618 LogPipe *
cfg_tree_new_pipe(CfgTree * self,LogExprNode * related_expr)619 cfg_tree_new_pipe(CfgTree *self, LogExprNode *related_expr)
620 {
621   LogPipe *pipe = log_pipe_new(self->cfg);
622   pipe->expr_node = related_expr;
623   g_ptr_array_add(self->initialized_pipes, pipe);
624   log_pipe_add_info(pipe, "cfg_tree_pipe");
625   return pipe;
626 }
627 
628 LogMultiplexer *
cfg_tree_new_mpx(CfgTree * self,LogExprNode * related_expr)629 cfg_tree_new_mpx(CfgTree *self, LogExprNode *related_expr)
630 {
631   LogMultiplexer *pipe = log_multiplexer_new(self->cfg);
632   pipe->super.expr_node = related_expr;
633   g_ptr_array_add(self->initialized_pipes, pipe);
634   return pipe;
635 }
636 
637 static gchar *
_format_anon_rule_name(CfgTree * self,gint content)638 _format_anon_rule_name(CfgTree *self, gint content)
639 {
640   return g_strdup_printf("#anon-%s%d", log_expr_node_get_content_name(content), self->anon_counters[content]++);
641 }
642 
643 /*
644  * Return the name of the rule that contains a LogExprNode. Generates
645  * one automatically for anonymous log expressions.
646  *
647  * NOTE: this returns an allocated string, the caller must free that.
648  */
649 gchar *
cfg_tree_get_rule_name(CfgTree * self,gint content,LogExprNode * node)650 cfg_tree_get_rule_name(CfgTree *self, gint content, LogExprNode *node)
651 {
652   gchar *rule_name;
653 
654   if (node)
655     {
656       LogExprNode *rule = log_expr_node_get_container_rule(node, content);
657       if (!rule->name)
658         rule->name = _format_anon_rule_name(self, content);
659       rule_name = g_strdup(rule->name);
660     }
661   else
662     {
663       rule_name = _format_anon_rule_name(self, content);
664     }
665 
666   return rule_name;
667 }
668 
669 /*
670  * Return a unique the name associated with a LogExprNode. It is of
671  * the format <rule>#<seqid>.
672  *
673  * NOTE: this returns an allocated string, the caller must free that.
674  */
675 gchar *
cfg_tree_get_child_id(CfgTree * self,gint content,LogExprNode * node)676 cfg_tree_get_child_id(CfgTree *self, gint content, LogExprNode *node)
677 {
678   gchar *rule_name = cfg_tree_get_rule_name(self, content, node);
679   gint cur_child_id;
680   gchar *res;
681 
682   if (node)
683     {
684       LogExprNode *rule = log_expr_node_get_container_rule(node, content);
685       cur_child_id = rule->child_id++;
686     }
687   else
688     cur_child_id = 0;
689   res = g_strdup_printf("%s#%d", rule_name, cur_child_id);
690   g_free(rule_name);
691   return res;
692 }
693 
694 /* hash foreach function to add all source objects to catch-all rules */
695 static void
cfg_tree_add_all_sources(gpointer key,gpointer value,gpointer user_data)696 cfg_tree_add_all_sources(gpointer key, gpointer value, gpointer user_data)
697 {
698   gpointer *args = (gpointer *) user_data;
699   LogExprNode *referring_rule = args[1];
700   LogExprNode *rule = (LogExprNode *) value;
701 
702   if (rule->content != ENC_SOURCE)
703     return;
704 
705   /* prepend a source reference */
706   referring_rule->children = log_expr_node_append_tail(log_expr_node_new_source_reference(rule->name, NULL),
707                                                        referring_rule->children);
708 }
709 
710 static gboolean
711 cfg_tree_compile_node(CfgTree *self, LogExprNode *node,
712                       LogPipe **outer_pipe_head, LogPipe **outer_pipe_tail);
713 
714 static gboolean
cfg_tree_compile_single(CfgTree * self,LogExprNode * node,LogPipe ** outer_pipe_head,LogPipe ** outer_pipe_tail)715 cfg_tree_compile_single(CfgTree *self, LogExprNode *node,
716                         LogPipe **outer_pipe_head, LogPipe **outer_pipe_tail)
717 {
718   LogPipe *pipe;
719 
720   g_assert(node->content == ENC_PIPE);
721 
722   /* LC_XXX flags are currently only implemented for sequences, ensure that the grammar enforces this. */
723   g_assert(node->flags == 0);
724 
725   pipe = node->object;
726 
727   if ((pipe->flags & PIF_INLINED) == 0)
728     {
729       /* first reference to the pipe uses the same instance, further ones will get cloned */
730       pipe->flags |= PIF_INLINED;
731       /* The pipe object is borrowed, so the reference counter must be increased. */
732       log_pipe_ref(pipe);
733     }
734   else
735     {
736       /* ok, we are using this pipe again, it needs to be cloned */
737       pipe = log_pipe_clone(pipe);
738       if (!pipe)
739         {
740           msg_error("Error cloning pipe into its reference point, probably the element in question is not meant to be used in this situation",
741                     log_expr_node_location_tag(node));
742           goto error;
743         }
744       pipe->flags |= PIF_INLINED;
745     }
746   g_ptr_array_add(self->initialized_pipes, pipe);
747   pipe->expr_node = node;
748 
749   if ((pipe->flags & PIF_SOURCE) == 0)
750     *outer_pipe_head = pipe;
751   *outer_pipe_tail = pipe;
752   return TRUE;
753 
754 error:
755   return FALSE;
756 }
757 
758 static gboolean
cfg_tree_compile_reference(CfgTree * self,LogExprNode * node,LogPipe ** outer_pipe_head,LogPipe ** outer_pipe_tail)759 cfg_tree_compile_reference(CfgTree *self, LogExprNode *node,
760                            LogPipe **outer_pipe_head, LogPipe **outer_pipe_tail)
761 {
762   LogExprNode *referenced_node;
763 
764   /* LC_XXX flags are currently only implemented for sequences, ensure that the grammar enforces this. */
765   g_assert(node->flags == 0);
766 
767   if (!node->object)
768     {
769       referenced_node = cfg_tree_get_object(self, node->content, node->name);
770     }
771   else
772     referenced_node = node->object;
773 
774   if (!referenced_node)
775     {
776       msg_error("Error resolving reference",
777                 evt_tag_str("content", log_expr_node_get_content_name(node->content)),
778                 evt_tag_str("name", node->name),
779                 log_expr_node_location_tag(node));
780       goto error;
781     }
782 
783   switch (referenced_node->content)
784     {
785     case ENC_SOURCE:
786     {
787       LogMultiplexer *mpx;
788       LogPipe *sub_pipe_head = NULL, *sub_pipe_tail = NULL;
789       LogPipe *attach_pipe = NULL;
790 
791       if (!referenced_node->aux)
792         {
793           if (!cfg_tree_compile_node(self, referenced_node, &sub_pipe_head, &sub_pipe_tail))
794             goto error;
795           log_expr_node_set_aux(referenced_node, log_pipe_ref(sub_pipe_tail), (GDestroyNotify) log_pipe_unref);
796         }
797       else
798         {
799           sub_pipe_tail = referenced_node->aux;
800         }
801 
802       attach_pipe = cfg_tree_new_pipe(self, node);
803 
804       if (sub_pipe_tail)
805         {
806           /* when the source is empty, we'll get a NULL tail in
807            * sub_pipe_tail.  We handle that by simply not connecting
808            * anything to the attachment point */
809 
810           if (!sub_pipe_tail->pipe_next)
811             {
812               mpx = cfg_tree_new_mpx(self, referenced_node);
813               log_pipe_add_info(&mpx->super, "mpx(source)");
814               log_pipe_append(sub_pipe_tail, &mpx->super);
815             }
816           else
817             {
818               mpx = (LogMultiplexer *) sub_pipe_tail->pipe_next;
819             }
820           log_multiplexer_add_next_hop(mpx, attach_pipe);
821         }
822       *outer_pipe_head = NULL;
823       *outer_pipe_tail = attach_pipe;
824       break;
825     }
826     case ENC_DESTINATION:
827     {
828       LogMultiplexer *mpx;
829       LogPipe *sub_pipe_head = NULL, *sub_pipe_tail = NULL;
830 
831       if (!referenced_node->aux)
832         {
833           if (!cfg_tree_compile_node(self, referenced_node, &sub_pipe_head, &sub_pipe_tail))
834             goto error;
835           log_expr_node_set_aux(referenced_node, log_pipe_ref(sub_pipe_head), (GDestroyNotify) log_pipe_unref);
836         }
837       else
838         {
839           sub_pipe_head = referenced_node->aux;
840         }
841 
842       /* We need a new LogMultiplexer instance for two reasons:
843 
844          1) we need to link something into the sequence, all
845          reference based destination invocations need a separate
846          LogPipe
847 
848          2) we have to fork downwards to the destination, it may
849          change the message but we need the original one towards
850          our next chain
851       */
852 
853       mpx = cfg_tree_new_mpx(self, node);
854       log_pipe_add_info(&mpx->super, "mpx(destination-reference)");
855 
856       if (sub_pipe_head)
857         {
858           /* when the destination is empty */
859           log_multiplexer_add_next_hop(mpx, sub_pipe_head);
860         }
861       *outer_pipe_head = &mpx->super;
862       *outer_pipe_tail = NULL;
863       break;
864     }
865     default:
866       return cfg_tree_compile_node(self, referenced_node, outer_pipe_head, outer_pipe_tail);
867     }
868   return TRUE;
869 
870 error:
871   return FALSE;
872 }
873 
874 static void
cfg_tree_propagate_expr_node_properties_to_pipe(LogExprNode * node,LogPipe * pipe)875 cfg_tree_propagate_expr_node_properties_to_pipe(LogExprNode *node, LogPipe *pipe)
876 {
877   if (node->flags & LC_FALLBACK)
878     pipe->flags |= PIF_BRANCH_FALLBACK;
879 
880   if (node->flags & LC_FINAL)
881     pipe->flags |= PIF_BRANCH_FINAL;
882 
883   if (node->flags & LC_FLOW_CONTROL)
884     pipe->flags |= PIF_HARD_FLOW_CONTROL;
885 
886   if (node->flags & LC_DROP_UNMATCHED)
887     pipe->flags |= PIF_DROP_UNMATCHED;
888 
889   if (!pipe->expr_node)
890     pipe->expr_node = node;
891 }
892 
893 /**
894  * cfg_tree_compile_sequence:
895  *
896  * Construct the sequential part of LogPipe pipeline as specified by
897  * the user. The sequential part is where no branches exist, pipes are
898  * merely linked to each other. This is in contrast with a "junction"
899  * where the processing is forked into different branches. Junctions
900  * are built using cfg_tree_compile_junction() above.
901  *
902  * The configuration is parsed into a series of LogExprNode
903  * elements, each giving a reference to a source, filter, parser,
904  * rewrite and destination. This function connects these so that their
905  * log_pipe_queue() method will dispatch the message correctly (which
906  * in turn boils down to setting the LogPipe->next member).
907  *
908  * The tree like structure is created using LogMultiplexer instances,
909  * pipes are connected back with a simple LogPipe instance that only
910  * forwards messages.
911  *
912  * The next member pointer is not holding a reference, but can be
913  * assumed to be kept alive as long as the configuration is running.
914  *
915  * Parameters:
916  * @self: the CfgTree instance
917  * @rule: the series of LogExprNode instances encapsulates as a LogExprNode
918  * @outer_pipe_tail: the last LogPipe to be used to chain further elements to this sequence
919  * @cfg: GlobalConfig instance
920  * @toplevel: whether this rule is a top-level one.
921  **/
922 static gboolean
cfg_tree_compile_sequence(CfgTree * self,LogExprNode * node,LogPipe ** outer_pipe_head,LogPipe ** outer_pipe_tail)923 cfg_tree_compile_sequence(CfgTree *self, LogExprNode *node,
924                           LogPipe **outer_pipe_head, LogPipe **outer_pipe_tail)
925 {
926   LogExprNode *ep;
927   LogPipe
928   *first_pipe,   /* the head of the constructed pipeline */
929   *last_pipe;    /* the current tail of the constructed pipeline */
930   LogPipe *source_join_pipe = NULL;
931   gboolean node_properties_propagated = FALSE;
932 
933   if ((node->flags & LC_CATCHALL) != 0)
934     {
935       /* the catch-all resolution code clears this flag */
936 
937       msg_error("Error in configuration, catch-all flag can only be specified for top-level log statements");
938       goto error;
939     }
940 
941   /* the loop below creates a sequence of LogPipe instances which
942    * essentially execute the user configuration once it is
943    * started.
944    *
945    * The input of this is a log expression, denoted by a tree of
946    * LogExprNode structures, built by the parser. We are storing the
947    * sequence as a linked list, pipes are linked with their "next"
948    * field.
949    *
950    * The head of this list is pointed to by @first_pipe, the current
951    * end is known as @last_pipe.
952    *
953    * In case the sequence starts with a source LogPipe (PIF_SOURCE
954    * flag), the head of the list is _not_ tracked, in that case
955    * first_pipe is NULL.
956    *
957    */
958 
959   first_pipe = last_pipe = NULL;
960 
961   for (ep = node->children; ep; ep = ep->next)
962     {
963       LogPipe *sub_pipe_head = NULL, *sub_pipe_tail = NULL;
964 
965       if (!cfg_tree_compile_node(self, ep, &sub_pipe_head, &sub_pipe_tail))
966         goto error;
967 
968       /* add pipe to the current pipe_line, e.g. after last_pipe, update last_pipe & first_pipe */
969       if (sub_pipe_head)
970         {
971           if (!node_properties_propagated)
972             {
973               cfg_tree_propagate_expr_node_properties_to_pipe(node, sub_pipe_head);
974               node_properties_propagated = TRUE;
975             }
976           if (!first_pipe && !last_pipe)
977             {
978               /* we only remember the first pipe in case we're not in
979                * source mode. In source mode, only last_pipe is set */
980 
981               first_pipe = sub_pipe_head;
982             }
983 
984           if (last_pipe)
985             {
986               g_assert(last_pipe->pipe_next == NULL);
987               log_pipe_append(last_pipe, sub_pipe_head);
988             }
989 
990           if (sub_pipe_tail)
991             {
992               last_pipe = sub_pipe_tail;
993             }
994           else
995             {
996               last_pipe = sub_pipe_head;
997               /* look for the final pipe */
998               while (last_pipe->pipe_next)
999                 {
1000                   last_pipe = last_pipe->pipe_next;
1001                 }
1002             }
1003           sub_pipe_head = NULL;
1004         }
1005       else if (sub_pipe_tail)
1006         {
1007           /* source pipe */
1008 
1009           if (first_pipe)
1010             {
1011               msg_error("Error compiling sequence, source-pipe follows a non-source one, please list source references/definitions first",
1012                         log_expr_node_location_tag(ep));
1013               goto error;
1014             }
1015 
1016           if (!source_join_pipe)
1017             {
1018               source_join_pipe = last_pipe = cfg_tree_new_pipe(self, node);
1019             }
1020           log_pipe_append(sub_pipe_tail, source_join_pipe);
1021         }
1022     }
1023 
1024 
1025   if (!first_pipe && !last_pipe)
1026     {
1027       /* this is an empty sequence, insert a do-nothing LogPipe */
1028       first_pipe = last_pipe = cfg_tree_new_pipe(self, node);
1029     }
1030 
1031 
1032   /* NOTE: if flow control is enabled, then we either need to have an
1033    * embedded log statement (in which case first_pipe is set, as we're not
1034    * starting with sources), OR we created a source_join_pipe already.
1035    *
1036    */
1037 
1038   g_assert(((node->flags & LC_FLOW_CONTROL) && (first_pipe || source_join_pipe)) ||
1039            !(node->flags & LC_FLOW_CONTROL));
1040 
1041   if (!node_properties_propagated)
1042     {
1043       /* we never encountered anything that would produce a head_pipe, e.g.
1044        * this sequence only contains a source and nothing else.  In that
1045        * case, apply node flags to the last pipe.  It should be picked up
1046        * when LogMultiplexer iterates over the branch in
1047        * log_multiplexer_init() as long as last_pipe is linked into the
1048        * pipe_next list and is not forked off at a LogMultiplexer.
1049        * */
1050 
1051       cfg_tree_propagate_expr_node_properties_to_pipe(node, last_pipe);
1052       node_properties_propagated = TRUE;
1053     }
1054 
1055   if (node->content == ENC_DESTINATION)
1056     {
1057       /* We want to leave pipe-next available to use for
1058          destinations. Config graph uses pipe-next to pass messages forward
1059          in a sequence layout. But pipe-next might be overridden (for
1060          example network destination with LogWriter) hence disjointing
1061          the config graph.
1062 
1063          This patch links destinations in T form, instead of single link.
1064 
1065          * (endpoint of sequence)
1066          |
1067          V
1068          * (multiplexer) -(next-hop)-> destination -(pipe-next*)-> logwriter
1069          |
1070          (pipe-next)
1071          |
1072          V
1073          * (rest of the sequence)
1074          That way destinations are free to use the pipe-next*
1075       */
1076 
1077       last_pipe = first_pipe;
1078     }
1079 
1080   *outer_pipe_tail = last_pipe;
1081   *outer_pipe_head = first_pipe;
1082   return TRUE;
1083 error:
1084 
1085   /* we don't need to free anything, everything we allocated is recorded in
1086    * @self, thus will be freed whenever cfg_tree_free is called */
1087 
1088   return FALSE;
1089 }
1090 
1091 /**
1092  * cfg_tree_compile_junction():
1093  *
1094  * This function builds a junction within the configuration. A
1095  * junction is where processing is forked into several branches, each
1096  * doing its own business, and then the end of each branch is
1097  * collected at the end so that further processing can be done on the
1098  * combined output of each log branch.
1099  *
1100  *       /-- branch --\
1101  *      /              \
1102  *  ---+---- branch ----+---
1103  *      \              /
1104  *       \-- branch --/
1105  **/
1106 static gboolean
cfg_tree_compile_junction(CfgTree * self,LogExprNode * node,LogPipe ** outer_pipe_head,LogPipe ** outer_pipe_tail)1107 cfg_tree_compile_junction(CfgTree *self,
1108                           LogExprNode *node,
1109                           LogPipe **outer_pipe_head, LogPipe **outer_pipe_tail)
1110 {
1111   LogExprNode *ep;
1112   LogPipe *join_pipe = NULL;    /* the pipe where parallel branches are joined in a junction */
1113   LogMultiplexer *fork_mpx = NULL;
1114 
1115   /* LC_XXX flags are currently only implemented for sequences, ensure that the grammar enforces this. */
1116   g_assert(node->flags == 0);
1117 
1118   for (ep = node->children; ep; ep = ep->next)
1119     {
1120       LogPipe *sub_pipe_head = NULL, *sub_pipe_tail = NULL;
1121       gboolean is_first_branch = (ep == node->children);
1122 
1123       if (!cfg_tree_compile_node(self, ep, &sub_pipe_head, &sub_pipe_tail))
1124         goto error;
1125 
1126       if (sub_pipe_head)
1127         {
1128           /* ep is an intermediate LogPipe or a destination, we have to fork */
1129 
1130           if (!is_first_branch && !fork_mpx)
1131             {
1132               msg_error("Error compiling junction, source and non-source branches are mixed",
1133                         log_expr_node_location_tag(ep));
1134               goto error;
1135             }
1136           if (!fork_mpx)
1137             {
1138               fork_mpx = cfg_tree_new_mpx(self, node);
1139               log_pipe_add_info(&fork_mpx->super, "mpx(junction)");
1140               *outer_pipe_head = &fork_mpx->super;
1141             }
1142           log_multiplexer_add_next_hop(fork_mpx, sub_pipe_head);
1143         }
1144       else
1145         {
1146           /* ep is a "source" LogPipe (cause no sub_pipe_head returned by compile_node). */
1147 
1148           if (fork_mpx)
1149             {
1150               msg_error("Error compiling junction, source and non-source branches are mixed",
1151                         log_expr_node_location_tag(ep));
1152               goto error;
1153             }
1154         }
1155 
1156       if (sub_pipe_tail && outer_pipe_tail)
1157         {
1158           if (!join_pipe)
1159             {
1160               join_pipe = cfg_tree_new_pipe(self, node);
1161             }
1162           log_pipe_append(sub_pipe_tail, join_pipe);
1163 
1164         }
1165     }
1166 
1167   if (outer_pipe_tail)
1168     *outer_pipe_tail = join_pipe;
1169   return TRUE;
1170 error:
1171 
1172   /* we don't need to free anything, everything we allocated is recorded in
1173    * @self, thus will be freed whenever cfg_tree_free is called */
1174 
1175   return FALSE;
1176 }
1177 
1178 /*
1179  * cfg_tree_compile_node:
1180  *
1181  * This function takes care of compiling a LogExprNode.
1182  *
1183  */
1184 gboolean
cfg_tree_compile_node(CfgTree * self,LogExprNode * node,LogPipe ** outer_pipe_head,LogPipe ** outer_pipe_tail)1185 cfg_tree_compile_node(CfgTree *self, LogExprNode *node,
1186                       LogPipe **outer_pipe_head, LogPipe **outer_pipe_tail)
1187 {
1188   gboolean result = FALSE;
1189   static gint indent = -1;
1190 
1191   if (trace_flag)
1192     {
1193       gchar buf[128];
1194       gchar compile_message[256];
1195 
1196       indent++;
1197       g_snprintf(compile_message, sizeof(compile_message),
1198                  "%-*sCompiling %s %s [%s] at [%s]",
1199                  indent * 2, "",
1200                  node->name ? : "#unnamed",
1201                  log_expr_node_get_layout_name(node->layout),
1202                  log_expr_node_get_content_name(node->content),
1203                  log_expr_node_format_location(node, buf, sizeof(buf)));
1204       msg_send_formatted_message(EVT_PRI_DEBUG, compile_message);
1205     }
1206 
1207   switch (node->layout)
1208     {
1209     case ENL_SINGLE:
1210       result = cfg_tree_compile_single(self, node, outer_pipe_head, outer_pipe_tail);
1211       break;
1212     case ENL_REFERENCE:
1213       result = cfg_tree_compile_reference(self, node, outer_pipe_head, outer_pipe_tail);
1214       break;
1215     case ENL_SEQUENCE:
1216       result = cfg_tree_compile_sequence(self, node, outer_pipe_head, outer_pipe_tail);
1217       break;
1218     case ENL_JUNCTION:
1219       result = cfg_tree_compile_junction(self, node, outer_pipe_head, outer_pipe_tail);
1220       break;
1221     default:
1222       g_assert_not_reached();
1223     }
1224 
1225   indent--;
1226   return result;
1227 }
1228 
1229 gboolean
cfg_tree_compile_rule(CfgTree * self,LogExprNode * rule)1230 cfg_tree_compile_rule(CfgTree *self, LogExprNode *rule)
1231 {
1232   LogPipe *sub_pipe_head = NULL, *sub_pipe_tail = NULL;
1233 
1234   return cfg_tree_compile_node(self, rule, &sub_pipe_head, &sub_pipe_tail);
1235 }
1236 
1237 static gboolean
cfg_tree_objects_equal(gconstpointer v1,gconstpointer v2)1238 cfg_tree_objects_equal(gconstpointer v1, gconstpointer v2)
1239 {
1240   LogExprNode *r1 = (LogExprNode *) v1;
1241   LogExprNode *r2 = (LogExprNode *) v2;
1242 
1243   if (r1->content != r2->content)
1244     return FALSE;
1245 
1246   /* we assume that only rules with a name are hashed */
1247 
1248   return strcmp(r1->name, r2->name) == 0;
1249 }
1250 
1251 static guint
cfg_tree_objects_hash(gconstpointer v)1252 cfg_tree_objects_hash(gconstpointer v)
1253 {
1254   LogExprNode *r = (LogExprNode *) v;
1255 
1256   /* we assume that only rules with a name are hashed */
1257   return r->content + g_str_hash(r->name);
1258 }
1259 
1260 gboolean
cfg_tree_add_object(CfgTree * self,LogExprNode * rule)1261 cfg_tree_add_object(CfgTree *self, LogExprNode *rule)
1262 {
1263   gboolean res = TRUE;
1264 
1265   if (rule->name)
1266     {
1267       /* only named rules can be stored as objects to be referenced later */
1268 
1269       /* check if already present */
1270       res = (g_hash_table_lookup(self->objects, rule) == NULL);
1271 
1272       /* key is the same as the object */
1273       g_hash_table_replace(self->objects, rule, rule);
1274     }
1275   else
1276     {
1277       /* unnamed rules are simply put in the rules array */
1278       g_ptr_array_add(self->rules, rule);
1279     }
1280 
1281   return res;
1282 }
1283 
1284 LogExprNode *
cfg_tree_get_object(CfgTree * self,gint content,const gchar * name)1285 cfg_tree_get_object(CfgTree *self, gint content, const gchar *name)
1286 {
1287   LogExprNode lookup_node;
1288 
1289   memset(&lookup_node, 0, sizeof(lookup_node));
1290   lookup_node.content = content;
1291   lookup_node.name = (gchar *) name;
1292 
1293   return g_hash_table_lookup(self->objects, &lookup_node);
1294 }
1295 
1296 GList *
cfg_tree_get_objects(CfgTree * self)1297 cfg_tree_get_objects(CfgTree *self)
1298 {
1299   return g_hash_table_get_values(self->objects);
1300 }
1301 
1302 gboolean
cfg_tree_add_template(CfgTree * self,LogTemplate * template)1303 cfg_tree_add_template(CfgTree *self, LogTemplate *template)
1304 {
1305   gboolean res = (g_hash_table_lookup(self->templates, template->name) == NULL);
1306   g_hash_table_replace(self->templates, template->name, template);
1307   return res;
1308 }
1309 
1310 LogTemplate *
cfg_tree_lookup_template(CfgTree * self,const gchar * name)1311 cfg_tree_lookup_template(CfgTree *self, const gchar *name)
1312 {
1313   if (name)
1314     return log_template_ref(g_hash_table_lookup(self->templates, name));
1315   return NULL;
1316 }
1317 
1318 LogTemplate *
cfg_tree_check_inline_template(CfgTree * self,const gchar * template_or_name,GError ** error)1319 cfg_tree_check_inline_template(CfgTree *self, const gchar *template_or_name, GError **error)
1320 {
1321   LogTemplate *template = cfg_tree_lookup_template(self, template_or_name);
1322 
1323   if (template == NULL)
1324     {
1325       template = log_template_new(self->cfg, NULL);
1326       if (!log_template_compile(template, template_or_name, error))
1327         {
1328           log_template_unref(template);
1329           return NULL;
1330         }
1331       template->def_inline = TRUE;
1332     }
1333   return template;
1334 }
1335 
1336 gboolean
cfg_tree_compile(CfgTree * self)1337 cfg_tree_compile(CfgTree *self)
1338 {
1339   gint i;
1340 
1341   /* resolve references within the configuration */
1342   if (self->compiled)
1343     return TRUE;
1344 
1345   for (i = 0; i < self->rules->len; i++)
1346     {
1347       LogExprNode *rule = (LogExprNode *) g_ptr_array_index(self->rules, i);
1348 
1349       if ((rule->flags & LC_CATCHALL))
1350         {
1351           gpointer args[] = { self, rule };
1352 
1353           g_hash_table_foreach(self->objects, cfg_tree_add_all_sources, args);
1354           rule->flags &= ~LC_CATCHALL;
1355         }
1356 
1357       if (!cfg_tree_compile_rule(self, rule))
1358         {
1359           return FALSE;
1360         }
1361     }
1362   self->compiled = TRUE;
1363   return TRUE;
1364 }
1365 
1366 static gboolean
_verify_unique_persist_names_among_pipes(const GPtrArray * initialized_pipes)1367 _verify_unique_persist_names_among_pipes(const GPtrArray *initialized_pipes)
1368 {
1369   GHashTable *pipe_persist_names = g_hash_table_new(g_str_hash, g_str_equal);
1370   gboolean result = TRUE;
1371 
1372   for (gint i = 0; i < initialized_pipes->len; ++i)
1373     {
1374       LogPipe *current_pipe = g_ptr_array_index(initialized_pipes, i);
1375       const gchar *current_pipe_name = log_pipe_get_persist_name(current_pipe);
1376 
1377       if (current_pipe_name != NULL)
1378         {
1379           if (g_hash_table_lookup_extended(pipe_persist_names, current_pipe_name, NULL, NULL))
1380             {
1381               msg_error("Error checking the uniqueness of the persist names, please override it "
1382                         "with persist-name option. Shutting down.",
1383                         evt_tag_str("persist_name", current_pipe_name),
1384                         log_pipe_location_tag(current_pipe), NULL);
1385               result = FALSE;
1386             }
1387           else
1388             {
1389               g_hash_table_replace(pipe_persist_names,
1390                                    (gpointer)current_pipe_name,
1391                                    (gpointer)current_pipe_name);
1392             }
1393         }
1394     }
1395 
1396   g_hash_table_destroy(pipe_persist_names);
1397 
1398   return result;
1399 }
1400 
1401 gboolean
cfg_tree_start(CfgTree * self)1402 cfg_tree_start(CfgTree *self)
1403 {
1404   gint i;
1405 
1406   if (!cfg_tree_compile(self))
1407     return FALSE;
1408 
1409   /*
1410    *   As there are pipes that are dynamically created during init, these
1411    *   pipes must be deinited before destroying the configuration, otherwise
1412    *   circular references will inhibit the free of the configuration
1413    *   structure.
1414    */
1415   for (i = 0; i < self->initialized_pipes->len; i++)
1416     {
1417       LogPipe *pipe = g_ptr_array_index(self->initialized_pipes, i);
1418 
1419       if (!log_pipe_init(pipe))
1420         {
1421           msg_error("Error initializing message pipeline",
1422                     evt_tag_str("plugin_name", pipe->plugin_name ? pipe->plugin_name : "not a plugin"),
1423                     log_pipe_location_tag(pipe));
1424           return FALSE;
1425         }
1426     }
1427 
1428   return _verify_unique_persist_names_among_pipes(self->initialized_pipes);
1429 }
1430 
1431 gboolean
cfg_tree_stop(CfgTree * self)1432 cfg_tree_stop(CfgTree *self)
1433 {
1434   gboolean success = TRUE;
1435   gint i;
1436 
1437   for (i = 0; i < self->initialized_pipes->len; i++)
1438     {
1439       if (!log_pipe_deinit(g_ptr_array_index(self->initialized_pipes, i)))
1440         success = FALSE;
1441     }
1442 
1443   return success;
1444 }
1445 
1446 gboolean
cfg_tree_on_inited(CfgTree * self)1447 cfg_tree_on_inited(CfgTree *self)
1448 {
1449   gint i;
1450 
1451   for (i = 0; i < self->initialized_pipes->len; i++)
1452     {
1453       LogPipe *pipe = g_ptr_array_index(self->initialized_pipes, i);
1454 
1455       if (!log_pipe_on_config_inited(pipe))
1456         {
1457           msg_error("Error executing on_config_inited hook",
1458                     evt_tag_str("plugin_name", pipe->plugin_name ? pipe->plugin_name : "not a plugin"),
1459                     log_pipe_location_tag(pipe));
1460           return FALSE;
1461         }
1462     }
1463 
1464   return TRUE;
1465 }
1466 
1467 void
cfg_tree_init_instance(CfgTree * self,GlobalConfig * cfg)1468 cfg_tree_init_instance(CfgTree *self, GlobalConfig *cfg)
1469 {
1470   memset(self, 0, sizeof(*self));
1471   self->initialized_pipes = g_ptr_array_new();
1472   self->objects = g_hash_table_new_full(cfg_tree_objects_hash, cfg_tree_objects_equal, NULL,
1473                                         (GDestroyNotify) log_expr_node_unref);
1474   self->templates = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify) log_template_unref);
1475   self->rules = g_ptr_array_new();
1476   self->cfg = cfg;
1477 }
1478 
1479 void
cfg_tree_free_instance(CfgTree * self)1480 cfg_tree_free_instance(CfgTree *self)
1481 {
1482   g_ptr_array_foreach(self->initialized_pipes, (GFunc) log_pipe_unref, NULL);
1483   g_ptr_array_free(self->initialized_pipes, TRUE);
1484 
1485   g_ptr_array_foreach(self->rules, (GFunc) log_expr_node_unref, NULL);
1486   g_ptr_array_free(self->rules, TRUE);
1487 
1488   g_hash_table_destroy(self->objects);
1489   g_hash_table_destroy(self->templates);
1490   self->cfg = NULL;
1491 }
1492