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-lexer.h"
26 #include "cfg-lexer-subst.h"
27 #include "cfg-block-generator.h"
28 #include "cfg-grammar.h"
29 #include "block-ref-parser.h"
30 #include "pragma-parser.h"
31 #include "messages.h"
32 #include "pathutils.h"
33 #include "plugin.h"
34 #include "plugin-types.h"
35
36 #include <string.h>
37 #include <glob.h>
38 #include <sys/stat.h>
39
40 /* include header for generated lexer */
41 #define YYSTYPE CFG_STYPE
42 #define YYLTYPE CFG_LTYPE
43 #include "cfg-lex.h"
44 #undef YYSTYPE
45 #undef YYLTYPE
46
47 /*
48 * A token block is a series of tokens to be injected into the tokens
49 * fetched by the lexer. It is assumed to be filled and then depleted, the
50 * two operations cannot be intermixed.
51 */
52 struct _CfgTokenBlock
53 {
54 gint pos;
55 GArray *tokens;
56 };
57
58 /**
59 * CfgLexerContext:
60 *
61 * This object tells the lexer in which context it is operating right
62 * now. The context influences the way the lexer works, for example in
63 * LL_CONTEXT_BLOCK_DEF/REF all keyword resolutions are disabled.
64 *
65 * A textual description is also associated with the current context
66 * in order to give better error messages.
67 **/
68 typedef struct _CfgLexerContext
69 {
70 gint type;
71 CfgLexerKeyword *keywords;
72 gchar desc[0];
73 } CfgLexerContext;
74
75 typedef enum
76 {
77 CLPR_ERROR,
78 CLPR_OK,
79 CLPR_LEX_AGAIN
80 } CfgLexerPreprocessResult;
81
82 /*
83 * cfg_lexer_push_context:
84 *
85 * This function can be used to push a lexer context to the stack. The top
86 * of the stack determines how an error is reported and can also influence
87 * the lexer.
88 */
89 void
cfg_lexer_push_context(CfgLexer * self,gint type,CfgLexerKeyword * keywords,const gchar * desc)90 cfg_lexer_push_context(CfgLexer *self, gint type, CfgLexerKeyword *keywords, const gchar *desc)
91 {
92 CfgLexerContext *context;
93
94 context = g_malloc(sizeof(CfgLexerContext) + strlen(desc) + 1);
95 context->type = type ? type : cfg_lexer_get_context_type(self);
96 context->keywords = keywords;
97 memcpy(&context->desc, desc, strlen(desc) + 1);
98 self->context_stack = g_list_prepend(self->context_stack, context);
99 }
100
101 /*
102 * cfg_lexer_pop_context:
103 *
104 * Pop the topmost item off the stack.
105 */
106 void
cfg_lexer_pop_context(CfgLexer * self)107 cfg_lexer_pop_context(CfgLexer *self)
108 {
109 if (self->context_stack)
110 {
111 g_free((gchar *) self->context_stack->data);
112 self->context_stack = g_list_delete_link(self->context_stack, self->context_stack);
113 }
114 }
115
116 /*
117 * cfg_lexer_get_context_type:
118 *
119 * Get the current context type (one of LL_CONTEXT_* values).
120 */
121 gint
cfg_lexer_get_context_type(CfgLexer * self)122 cfg_lexer_get_context_type(CfgLexer *self)
123 {
124 GList *l;
125
126 l = self->context_stack;
127 if (l)
128 return ((CfgLexerContext *) l->data)->type;
129 return 0;
130 }
131
132 /*
133 * cfg_lexer_get_context_description:
134 *
135 * Get the description of the current context.
136 */
137 const gchar *
cfg_lexer_get_context_description(CfgLexer * self)138 cfg_lexer_get_context_description(CfgLexer *self)
139 {
140 GList *l;
141
142 l = self->context_stack;
143 if (l)
144 return ((CfgLexerContext *) l->data)->desc;
145 return "configuration";
146 }
147
148 /* this can only be called from the grammar */
149 static CfgIncludeLevel *
_find_closest_file_inclusion(CfgLexer * self,CFG_LTYPE * yylloc)150 _find_closest_file_inclusion(CfgLexer *self, CFG_LTYPE *yylloc)
151 {
152 for (gint level_ndx = self->include_depth; level_ndx >= 0; level_ndx--)
153 {
154 CfgIncludeLevel *level = &self->include_stack[level_ndx];
155
156 if (level->include_type == CFGI_FILE)
157 return level;
158 }
159 return NULL;
160 }
161
162 const gchar *
cfg_lexer_format_location(CfgLexer * self,CFG_LTYPE * yylloc,gchar * buf,gsize buf_len)163 cfg_lexer_format_location(CfgLexer *self, CFG_LTYPE *yylloc, gchar *buf, gsize buf_len)
164 {
165 CfgIncludeLevel *level;
166
167 level = _find_closest_file_inclusion(self, yylloc);
168 if (level)
169 g_snprintf(buf, buf_len, "%s:%d:%d",
170 level->name,
171 level->lloc.first_line, level->lloc.first_column);
172 else
173 g_snprintf(buf, buf_len, "%s:%d:%d", "#buffer", yylloc->first_line, yylloc->first_column);
174 return buf;
175 }
176
177 EVTTAG *
cfg_lexer_format_location_tag(CfgLexer * self,CFG_LTYPE * yylloc)178 cfg_lexer_format_location_tag(CfgLexer *self, CFG_LTYPE *yylloc)
179 {
180 gchar buf[256];
181
182 return evt_tag_str("location", cfg_lexer_format_location(self, yylloc, buf, sizeof(buf)));
183 }
184
185 int
cfg_lexer_lookup_keyword(CfgLexer * self,CFG_STYPE * yylval,CFG_LTYPE * yylloc,const char * token)186 cfg_lexer_lookup_keyword(CfgLexer *self, CFG_STYPE *yylval, CFG_LTYPE *yylloc, const char *token)
187 {
188 GList *l;
189
190 l = self->context_stack;
191 while (l)
192 {
193 CfgLexerContext *context = ((CfgLexerContext *) l->data);
194 CfgLexerKeyword *keywords = context->keywords;
195
196 if (keywords)
197 {
198 int i, j;
199
200 for (i = 0; keywords[i].kw_name; i++)
201 {
202 if (strcmp(keywords[i].kw_name, CFG_KEYWORD_STOP) == 0)
203 {
204 yylval->type = LL_IDENTIFIER;
205 yylval->cptr = strdup(token);
206 return LL_IDENTIFIER;
207 }
208
209 for (j = 0; token[j] && keywords[i].kw_name[j]; j++)
210 {
211 if (token[j] == '-' || token[j] == '_')
212 {
213 if (keywords[i].kw_name[j] != '_')
214 break;
215 }
216 else if (token[j] != keywords[i].kw_name[j])
217 break;
218 }
219 if (token[j] == 0 && keywords[i].kw_name[j] == 0)
220 {
221 /* match */
222 switch (keywords[i].kw_status)
223 {
224 case KWS_OBSOLETE:
225 msg_warning("WARNING: Your configuration file uses an obsoleted keyword, please update your configuration",
226 evt_tag_str("keyword", keywords[i].kw_name),
227 evt_tag_str("change", keywords[i].kw_explain),
228 cfg_lexer_format_location_tag(self, yylloc));
229 break;
230 default:
231 break;
232 }
233 keywords[i].kw_status = KWS_NORMAL;
234 yylval->type = LL_TOKEN;
235 yylval->token = keywords[i].kw_token;
236 return keywords[i].kw_token;
237 }
238 }
239 }
240 l = l->next;
241 }
242 yylval->type = LL_IDENTIFIER;
243 yylval->cptr = strdup(token);
244 return LL_IDENTIFIER;
245 }
246
247 void
cfg_lexer_clear_include_level(CfgLexer * self,CfgIncludeLevel * level)248 cfg_lexer_clear_include_level(CfgLexer *self, CfgIncludeLevel *level)
249 {
250 g_free(level->name);
251 if (level->yybuf)
252 _cfg_lexer__delete_buffer(level->yybuf, self->state);
253
254 if (level->include_type == CFGI_FILE)
255 {
256 if (level->file.include_file)
257 fclose(level->file.include_file);
258 g_slist_foreach(level->file.files, (GFunc) g_free, NULL);
259 g_slist_free(level->file.files);
260 }
261 else if (level->include_type == CFGI_BUFFER)
262 {
263 g_free(level->buffer.content);
264 g_free(level->buffer.original_content);
265 }
266 memset(level, 0, sizeof(*level));
267 }
268
269 gboolean
cfg_lexer_start_next_include(CfgLexer * self)270 cfg_lexer_start_next_include(CfgLexer *self)
271 {
272 CfgIncludeLevel *level = &self->include_stack[self->include_depth];
273 gchar *filename;
274 gboolean buffer_processed = FALSE;
275
276 if (self->include_depth == 0)
277 {
278 return FALSE;
279 }
280
281 if (level->yybuf)
282 {
283 msg_debug("Finishing include",
284 evt_tag_str((level->include_type == CFGI_FILE ? "filename" : "content"), level->name),
285 evt_tag_int("depth", self->include_depth));
286 buffer_processed = TRUE;
287 }
288
289 /* reset the include state, should also handle initial invocations, in which case everything is NULL */
290 if (level->yybuf)
291 {
292 _cfg_lexer__delete_buffer(level->yybuf, self->state);
293 level->yybuf = NULL;
294 }
295
296 if (level->include_type == CFGI_FILE)
297 {
298 if (level->file.include_file)
299 {
300 fclose(level->file.include_file);
301 level->file.include_file = NULL;
302 }
303 }
304
305 if ((level->include_type == CFGI_BUFFER && buffer_processed) ||
306 (level->include_type == CFGI_FILE && !level->file.files))
307 {
308 /* we finished with an include statement that included a series of
309 * files (e.g. directory include). */
310
311
312 /* NOTE: this couple of lines should become just a call to
313 * cfg_lexer_clear_include_level(), however this entire function is
314 * playing nasty tricks with the data members within the
315 * CfgIncludeLevel, which I can't decipher right now, so I am leaving
316 * this as is. Memory management in the lexer is clearly messed
317 * up. */
318
319 g_free(level->name);
320
321 if (level->include_type == CFGI_BUFFER)
322 {
323 g_free(level->buffer.content);
324 g_free(level->buffer.original_content);
325 }
326
327 memset(level, 0, sizeof(*level));
328
329 self->include_depth--;
330 _cfg_lexer__switch_to_buffer(self->include_stack[self->include_depth].yybuf, self->state);
331
332 return TRUE;
333 }
334
335 /* now populate "level" with the new include information */
336 if (level->include_type == CFGI_BUFFER)
337 {
338 level->yybuf = _cfg_lexer__scan_buffer(level->buffer.content, level->buffer.content_length, self->state);
339 }
340 else if (level->include_type == CFGI_FILE)
341 {
342 FILE *include_file;
343
344 filename = (gchar *) level->file.files->data;
345 level->file.files = g_slist_delete_link(level->file.files, level->file.files);
346
347 include_file = fopen(filename, "r");
348 if (!include_file)
349 {
350 msg_error("Error opening include file",
351 evt_tag_str("filename", filename),
352 evt_tag_int("depth", self->include_depth));
353 g_free(filename);
354 return FALSE;
355 }
356 msg_debug("Starting to read include file",
357 evt_tag_str("filename", filename),
358 evt_tag_int("depth", self->include_depth));
359 g_free(level->name);
360 level->name = filename;
361
362 level->file.include_file = include_file;
363 level->yybuf = _cfg_lexer__create_buffer(level->file.include_file, YY_BUF_SIZE, self->state);
364 }
365 else
366 {
367 g_assert_not_reached();
368 }
369
370 level->lloc.first_line = level->lloc.last_line = 1;
371 level->lloc.first_column = level->lloc.last_column = 1;
372 level->lloc.level = level;
373
374 _cfg_lexer__switch_to_buffer(level->yybuf, self->state);
375 return TRUE;
376 }
377
378 static gboolean
cfg_lexer_include_file_simple(CfgLexer * self,const gchar * filename)379 cfg_lexer_include_file_simple(CfgLexer *self, const gchar *filename)
380 {
381 CfgIncludeLevel *level;
382 struct stat st;
383
384 if (stat(filename, &st) < 0)
385 {
386 return FALSE;
387 }
388
389 self->include_depth++;
390 level = &self->include_stack[self->include_depth];
391 level->include_type = CFGI_FILE;
392 if (S_ISDIR(st.st_mode))
393 {
394 GDir *dir;
395 GError *error = NULL;
396 const gchar *entry;
397
398 dir = g_dir_open(filename, 0, &error);
399 if (!dir)
400 {
401 msg_error("Error opening directory for reading",
402 evt_tag_str("filename", filename),
403 evt_tag_str("error", error->message));
404 g_error_free(error);
405 goto drop_level;
406 }
407 while ((entry = g_dir_read_name(dir)))
408 {
409 const gchar *p;
410 if (entry[0] == '.')
411 {
412 msg_debug("Skipping include file, it cannot begin with .",
413 evt_tag_str("filename", entry));
414 continue;
415 }
416 for (p = entry; *p; p++)
417 {
418 if (!((*p >= 'a' && *p <= 'z') ||
419 (*p >= 'A' && *p <= 'Z') ||
420 (*p >= '0' && *p <= '9') ||
421 (*p == '_') || (*p == '-') || (*p == '.')))
422 {
423 msg_debug("Skipping include file, does not match pattern [\\-_a-zA-Z0-9]+",
424 evt_tag_str("filename", entry));
425 p = NULL;
426 break;
427 }
428 }
429 if (p)
430 {
431 gchar *full_filename = g_build_filename(filename, entry, NULL);
432 if (stat(full_filename, &st) < 0 || S_ISDIR(st.st_mode))
433 {
434 msg_debug("Skipping include file as it is a directory",
435 evt_tag_str("filename", entry));
436 g_free(full_filename);
437 continue;
438 }
439 level->file.files = g_slist_insert_sorted(level->file.files, full_filename, (GCompareFunc) strcmp);
440 msg_debug("Adding include file",
441 evt_tag_str("filename", entry),
442 evt_tag_int("depth", self->include_depth));
443 }
444 }
445 g_dir_close(dir);
446 if (!level->file.files)
447 {
448 /* no include files in the specified directory */
449 msg_debug("No files in this include directory",
450 evt_tag_str("dir", filename));
451 self->include_depth--;
452 return TRUE;
453 }
454 }
455 else
456 {
457 g_assert(level->file.files == NULL);
458 level->file.files = g_slist_prepend(level->file.files, g_strdup(filename));
459 }
460 return cfg_lexer_start_next_include(self);
461 drop_level:
462 g_slist_foreach(level->file.files, (GFunc) g_free, NULL);
463 g_slist_free(level->file.files);
464 level->file.files = NULL;
465
466 return FALSE;
467 }
468
469 static int
_cfg_lexer_glob_err(const char * p,gint e)470 _cfg_lexer_glob_err (const char *p, gint e)
471 {
472 if (e != ENOENT)
473 {
474 msg_debug ("Error processing path for inclusion",
475 evt_tag_str("path", p),
476 evt_tag_errno("errno", e));
477 return -1;
478 }
479 return 0;
480 }
481
482 #ifndef GLOB_NOMAGIC
483 #define GLOB_NOMAGIC 0
484
485 int
__glob_pattern_p(const char * pattern)486 __glob_pattern_p (const char *pattern)
487 {
488 register const char *p;
489 int open = 0;
490
491 for (p = pattern; *p != '\0'; ++p)
492 switch (*p)
493 {
494 case '?':
495 case '*':
496 return 1;
497
498 case '\\':
499 if (p[1] != '\0')
500 ++p;
501 break;
502
503 case '[':
504 open = 1;
505 break;
506
507 case ']':
508 if (open)
509 return 1;
510 break;
511 }
512
513 return 0;
514 }
515 #else
516 #define SYSLOG_NG_HAVE_GLOB_NOMAGIC 1
517 #endif
518
519 static gboolean
cfg_lexer_include_file_add(CfgLexer * self,const gchar * fn)520 cfg_lexer_include_file_add(CfgLexer *self, const gchar *fn)
521 {
522 CfgIncludeLevel *level;
523
524 level = &self->include_stack[self->include_depth];
525 level->include_type = CFGI_FILE;
526
527 level->file.files = g_slist_insert_sorted(level->file.files,
528 strdup(fn),
529 (GCompareFunc) strcmp);
530
531 msg_debug("Adding include file",
532 evt_tag_str("filename", fn),
533 evt_tag_int("depth", self->include_depth));
534
535 return TRUE;
536 }
537
538 static gboolean
cfg_lexer_include_file_glob_at(CfgLexer * self,const gchar * pattern)539 cfg_lexer_include_file_glob_at(CfgLexer *self, const gchar *pattern)
540 {
541 glob_t globbuf;
542 size_t i;
543 int r;
544
545 r = glob(pattern, GLOB_NOMAGIC, _cfg_lexer_glob_err, &globbuf);
546
547 if (r != 0)
548 {
549 globfree(&globbuf);
550 if (r == GLOB_NOMATCH)
551 {
552 #ifndef SYSLOG_NG_HAVE_GLOB_NOMAGIC
553 if (!__glob_pattern_p (pattern))
554 {
555 return cfg_lexer_include_file_add(self, pattern);
556 }
557 #endif
558 return FALSE;
559 }
560 return TRUE;
561 }
562
563 for (i = 0; i < globbuf.gl_pathc; i++)
564 {
565 cfg_lexer_include_file_add(self, globbuf.gl_pathv[i]);
566 }
567
568 globfree(&globbuf);
569
570 return TRUE;
571 }
572
573 static const gchar *
_get_include_path(CfgLexer * self)574 _get_include_path(CfgLexer *self)
575 {
576 return self->cfg ? cfg_args_get(self->cfg->globals, "include-path") : NULL;
577 }
578
579 static gboolean
cfg_lexer_include_file_glob(CfgLexer * self,const gchar * filename_)580 cfg_lexer_include_file_glob(CfgLexer *self, const gchar *filename_)
581 {
582 const gchar *path = _get_include_path(self);
583 gboolean process = FALSE;
584
585 self->include_depth++;
586
587 if (filename_[0] == '/' || !path)
588 process = cfg_lexer_include_file_glob_at(self, filename_);
589 else
590 {
591 gchar **dirs;
592 gchar *cf;
593 gint i = 0;
594
595 dirs = g_strsplit(path, G_SEARCHPATH_SEPARATOR_S, 0);
596 while (dirs && dirs[i])
597 {
598 cf = g_build_filename(dirs[i], filename_, NULL);
599 process |= cfg_lexer_include_file_glob_at(self, cf);
600 g_free(cf);
601 i++;
602 }
603 g_strfreev(dirs);
604 }
605 if (process)
606 {
607 return cfg_lexer_start_next_include(self);
608 }
609 else
610 {
611 self->include_depth--;
612 return TRUE;
613 }
614 }
615
616 gboolean
cfg_lexer_include_file(CfgLexer * self,const gchar * filename_)617 cfg_lexer_include_file(CfgLexer *self, const gchar *filename_)
618 {
619 struct stat st;
620 gchar *filename;
621
622 msg_debug("Processing @include statement",
623 evt_tag_str("filename", filename_),
624 evt_tag_str("include-path", _get_include_path(self)));
625
626 if (self->include_depth >= MAX_INCLUDE_DEPTH - 1)
627 {
628 msg_error("Include file depth is too deep, increase MAX_INCLUDE_DEPTH and recompile",
629 evt_tag_str("filename", filename_),
630 evt_tag_int("depth", self->include_depth));
631 return FALSE;
632 }
633
634 filename = find_file_in_path(_get_include_path(self), filename_, G_FILE_TEST_EXISTS);
635 if (!filename || stat(filename, &st) < 0)
636 {
637 if (filename)
638 g_free(filename);
639
640 if (cfg_lexer_include_file_glob(self, filename_))
641 return TRUE;
642
643 msg_error("Include file/directory not found",
644 evt_tag_str("filename", filename_),
645 evt_tag_str("include-path", _get_include_path(self)),
646 evt_tag_error("error"));
647 return FALSE;
648 }
649 else
650 {
651 gboolean result;
652
653 result = cfg_lexer_include_file_simple(self, filename);
654 g_free(filename);
655 return result;
656 }
657 }
658
659 gboolean
cfg_lexer_include_buffer_without_backtick_substitution(CfgLexer * self,const gchar * name,const gchar * buffer,gsize length)660 cfg_lexer_include_buffer_without_backtick_substitution(CfgLexer *self, const gchar *name, const gchar *buffer,
661 gsize length)
662 {
663 CfgIncludeLevel *level;
664 gchar *lexer_buffer;
665 gsize lexer_buffer_len;
666
667 g_assert(length >= 0);
668
669 if (self->include_depth >= MAX_INCLUDE_DEPTH - 1)
670 {
671 msg_error("Include file depth is too deep, increase MAX_INCLUDE_DEPTH and recompile",
672 evt_tag_str("buffer", name),
673 evt_tag_int("depth", self->include_depth));
674 return FALSE;
675 }
676
677 /* lex requires two NUL characters at the end of the input */
678 lexer_buffer_len = length + 2;
679 lexer_buffer = g_malloc(lexer_buffer_len);
680 memcpy(lexer_buffer, buffer, length);
681 lexer_buffer[length] = 0;
682 lexer_buffer[length + 1] = 0;
683
684 self->include_depth++;
685 level = &self->include_stack[self->include_depth];
686
687 level->include_type = CFGI_BUFFER;
688 level->buffer.content = lexer_buffer;
689 level->buffer.content_length = lexer_buffer_len;
690 level->buffer.original_content = g_strdup(lexer_buffer);
691 level->name = g_strdup(name);
692
693 return cfg_lexer_start_next_include(self);
694 }
695
696 /* NOTE: if length is negative, it indicates zero-terminated buffer and
697 * length should be determined based on that */
698 gboolean
cfg_lexer_include_buffer(CfgLexer * self,const gchar * name,const gchar * buffer,gssize length)699 cfg_lexer_include_buffer(CfgLexer *self, const gchar *name, const gchar *buffer, gssize length)
700 {
701 gchar *substituted_buffer;
702 gsize substituted_length = 0;
703 GError *error = NULL;
704 gboolean result = FALSE;
705
706 substituted_buffer = cfg_lexer_subst_args_in_input(self->cfg ? self->cfg->globals : NULL, NULL, NULL, buffer, length,
707 &substituted_length, &error);
708 if (!substituted_buffer)
709 {
710 msg_error("Error resolving backtick references in block or buffer",
711 evt_tag_str("buffer", name),
712 evt_tag_str("error", error->message));
713 g_clear_error(&error);
714 return FALSE;
715 }
716
717 result = cfg_lexer_include_buffer_without_backtick_substitution(self, name, substituted_buffer, substituted_length);
718 g_free(substituted_buffer);
719 return result;
720 }
721
722 void
cfg_lexer_inject_token_block(CfgLexer * self,CfgTokenBlock * block)723 cfg_lexer_inject_token_block(CfgLexer *self, CfgTokenBlock *block)
724 {
725 self->token_blocks = g_list_append(self->token_blocks, block);
726 }
727
728
729 typedef struct _GeneratorPlugin
730 {
731 Plugin super;
732 CfgBlockGenerator *gen;
733 } GeneratorPlugin;
734
735 static gpointer
_generator_plugin_construct(Plugin * s)736 _generator_plugin_construct(Plugin *s)
737 {
738 GeneratorPlugin *self = (GeneratorPlugin *) s;
739
740 return cfg_block_generator_ref(self->gen);
741 }
742
743 static void
_generator_plugin_free(Plugin * s)744 _generator_plugin_free(Plugin *s)
745 {
746 GeneratorPlugin *self = (GeneratorPlugin *) s;
747
748 cfg_block_generator_unref(self->gen);
749 g_free((gchar *) self->super.name);
750 g_free(s);
751 }
752
753 void
cfg_lexer_register_generator_plugin(PluginContext * context,CfgBlockGenerator * gen)754 cfg_lexer_register_generator_plugin(PluginContext *context, CfgBlockGenerator *gen)
755 {
756 GeneratorPlugin *plugin = g_new0(GeneratorPlugin, 1);
757
758 plugin->super.type = gen->context | LL_CONTEXT_FLAG_GENERATOR;
759 plugin->super.name = g_strdup(gen->name);
760 plugin->super.free_fn = _generator_plugin_free;
761 plugin->super.construct = _generator_plugin_construct;
762 plugin->super.parser = &block_ref_parser;
763 plugin->gen = gen;
764
765 plugin_register(context, &plugin->super, 1);
766 }
767
768 static gboolean
_is_generator_plugin(Plugin * p)769 _is_generator_plugin(Plugin *p)
770 {
771 return p->type & LL_CONTEXT_FLAG_GENERATOR;
772 }
773
774 static Plugin *
cfg_lexer_find_generator_plugin(CfgLexer * self,GlobalConfig * cfg,gint context,const gchar * name)775 cfg_lexer_find_generator_plugin(CfgLexer *self, GlobalConfig *cfg, gint context, const gchar *name)
776 {
777 Plugin *p;
778
779 p = plugin_find(&cfg->plugin_context, context | LL_CONTEXT_FLAG_GENERATOR, name);
780 if (!p || !_is_generator_plugin(p))
781 return NULL;
782
783 return p;
784 }
785
786 static CFG_STYPE
cfg_lexer_copy_token(const CFG_STYPE * original)787 cfg_lexer_copy_token(const CFG_STYPE *original)
788 {
789 CFG_STYPE dest;
790 int type = original->type;
791 dest.type = type;
792
793 if (type == LL_TOKEN)
794 {
795 dest.token = original->token;
796 }
797 else if (type == LL_IDENTIFIER ||
798 type == LL_STRING ||
799 type == LL_BLOCK)
800 {
801 dest.cptr = strdup(original->cptr);
802 }
803 else if (type == LL_NUMBER)
804 {
805 dest.num = original->num;
806 }
807 else if (type == LL_FLOAT)
808 {
809 dest.fnum = original->fnum;
810 }
811
812 return dest;
813 }
814
815 void
cfg_lexer_unput_token(CfgLexer * self,CFG_STYPE * yylval)816 cfg_lexer_unput_token(CfgLexer *self, CFG_STYPE *yylval)
817 {
818 CfgTokenBlock *block;
819
820 block = cfg_token_block_new();
821 cfg_token_block_add_token(block, yylval);
822 cfg_lexer_inject_token_block(self, block);
823 }
824
825 /*
826 * NOTE: the caller is expected to manage the CFG_STYPE instance itself (as
827 * this is the way it is defined by the lexer), this function only frees its
828 * contents.
829 */
830 void
cfg_lexer_free_token(CFG_STYPE * token)831 cfg_lexer_free_token(CFG_STYPE *token)
832 {
833 if (token->type == LL_STRING || token->type == LL_IDENTIFIER || token->type == LL_BLOCK)
834 free(token->cptr);
835 }
836
837 static int
_invoke__cfg_lexer_lex(CfgLexer * self,CFG_STYPE * yylval,CFG_LTYPE * yylloc)838 _invoke__cfg_lexer_lex(CfgLexer *self, CFG_STYPE *yylval, CFG_LTYPE *yylloc)
839 {
840 if (setjmp(self->fatal_error))
841 {
842 CFG_LTYPE *cur_lloc = &self->include_stack[self->include_depth].lloc;
843
844 *yylloc = *cur_lloc;
845 return LL_ERROR;
846 }
847 return _cfg_lexer_lex(yylval, yylloc, self->state);
848 }
849
850 static gboolean
cfg_lexer_consume_next_injected_token(CfgLexer * self,gint * tok,CFG_STYPE * yylval,CFG_LTYPE * yylloc)851 cfg_lexer_consume_next_injected_token(CfgLexer *self, gint *tok, CFG_STYPE *yylval, CFG_LTYPE *yylloc)
852 {
853 CfgTokenBlock *block;
854 CFG_STYPE *token;
855
856 while (self->token_blocks)
857 {
858 block = self->token_blocks->data;
859 token = cfg_token_block_get_token(block);
860
861 if (token)
862 {
863 *yylval = *token;
864 *yylloc = self->include_stack[self->include_depth].lloc;
865
866 if (token->type == LL_TOKEN)
867 *tok = token->token;
868 else
869 *tok = token->type;
870
871 return TRUE;
872 }
873 else
874 {
875 self->token_blocks = g_list_delete_link(self->token_blocks, self->token_blocks);
876 cfg_token_block_free(block);
877 }
878 }
879
880 return FALSE;
881 }
882
883 static gint
cfg_lexer_lex_next_token(CfgLexer * self,CFG_STYPE * yylval,CFG_LTYPE * yylloc)884 cfg_lexer_lex_next_token(CfgLexer *self, CFG_STYPE *yylval, CFG_LTYPE *yylloc)
885 {
886 yylval->type = 0;
887
888 g_string_truncate(self->token_text, 0);
889 g_string_truncate(self->token_pretext, 0);
890
891 gint tok = _invoke__cfg_lexer_lex(self, yylval, yylloc);
892 if (yylval->type == 0)
893 yylval->type = tok;
894
895 return tok;
896 }
897
898 static void
cfg_lexer_append_preprocessed_output(CfgLexer * self,const gchar * token_text)899 cfg_lexer_append_preprocessed_output(CfgLexer *self, const gchar *token_text)
900 {
901 if (self->preprocess_output)
902 g_string_append_printf(self->preprocess_output, "%s", token_text);
903 }
904
905 static gboolean
cfg_lexer_parse_and_run_block_generator(CfgLexer * self,Plugin * p,CFG_STYPE * yylval)906 cfg_lexer_parse_and_run_block_generator(CfgLexer *self, Plugin *p, CFG_STYPE *yylval)
907 {
908 gpointer *args = NULL;
909 CfgIncludeLevel *level = &self->include_stack[self->include_depth];
910 CfgBlockGenerator *gen = plugin_construct(p);
911 gboolean success = TRUE;
912
913 self->preprocess_suppress_tokens++;
914
915 gint saved_line = level->lloc.first_line;
916 gint saved_column = level->lloc.first_column;
917 CfgParser *gen_parser = p->parser;
918 if (gen_parser && !cfg_parser_parse(gen_parser, self, (gpointer *) &args, NULL))
919 {
920 cfg_parser_cleanup(gen_parser, args);
921
922 level->lloc.first_line = saved_line;
923 level->lloc.first_column = saved_column;
924 free(yylval->cptr);
925 self->preprocess_suppress_tokens--;
926
927 success = FALSE;
928 goto exit;
929 }
930
931 GString *result = g_string_sized_new(256);
932 gchar buf[256];
933 level->lloc.first_line = saved_line;
934 level->lloc.first_column = saved_column;
935 self->preprocess_suppress_tokens--;
936 success = cfg_block_generator_generate(gen, self->cfg, args, result,
937 cfg_lexer_format_location(self, &level->lloc, buf, sizeof(buf)));
938
939 free(yylval->cptr);
940 cfg_parser_cleanup(gen_parser, args);
941
942 if (!success)
943 {
944 g_string_free(result, TRUE);
945
946 success = FALSE;
947 goto exit;
948 }
949
950 cfg_block_generator_format_name(gen, buf, sizeof(buf));
951
952 if (gen->suppress_backticks)
953 success = cfg_lexer_include_buffer_without_backtick_substitution(self, buf, result->str, result->len);
954 else
955 success = cfg_lexer_include_buffer(self, buf, result->str, result->len);
956 g_string_free(result, TRUE);
957
958 exit:
959 cfg_block_generator_unref(gen);
960 return success;
961 }
962
963 static gboolean
cfg_lexer_parse_pragma(CfgLexer * self)964 cfg_lexer_parse_pragma(CfgLexer *self)
965 {
966 gpointer dummy;
967 CfgIncludeLevel *level = &self->include_stack[self->include_depth];
968
969 cfg_lexer_append_preprocessed_output(self, "@");
970
971 gint saved_line = level->lloc.first_line;
972 gint saved_column = level->lloc.first_column;
973
974 if (!cfg_parser_parse(&pragma_parser, self, &dummy, NULL))
975 {
976 level->lloc.first_line = saved_line;
977 level->lloc.first_column = saved_column;
978 return FALSE;
979 }
980
981 return TRUE;
982 }
983
984 static CfgLexerPreprocessResult
cfg_lexer_preprocess(CfgLexer * self,gint tok,CFG_STYPE * yylval,CFG_LTYPE * yylloc)985 cfg_lexer_preprocess(CfgLexer *self, gint tok, CFG_STYPE *yylval, CFG_LTYPE *yylloc)
986 {
987 /*
988 * NOTE:
989 *
990 * This code is deeply coupled with GlobalConfig and most of it does
991 * not make sense to execute if self->cfg is NULL. Thus, some of the
992 * conditionals contain an explicit self->cfg check, in other cases it is
993 * implicitly checked by the first conditional of a series of if-then-else
994 * statements.
995 *
996 */
997
998 Plugin *p;
999
1000 if (tok == LL_IDENTIFIER &&
1001 self->cfg &&
1002 (p = cfg_lexer_find_generator_plugin(self, self->cfg, cfg_lexer_get_context_type(self), yylval->cptr)))
1003 {
1004 if (!cfg_lexer_parse_and_run_block_generator(self, p, yylval))
1005 return CLPR_ERROR;
1006
1007 return CLPR_LEX_AGAIN;
1008 }
1009
1010 if (self->ignore_pragma || self->cfg == NULL)
1011 {
1012 /* only process @pragma/@include tokens in case pragma allowed is set
1013 * and the associated configuration is not NULL */
1014 ;
1015 }
1016 else if (tok == LL_PRAGMA)
1017 {
1018 if (!cfg_lexer_parse_pragma(self))
1019 return CLPR_ERROR;
1020
1021 return CLPR_LEX_AGAIN;
1022 }
1023 else if (cfg_lexer_get_context_type(self) != LL_CONTEXT_PRAGMA && !self->non_pragma_seen)
1024 {
1025 /* first non-pragma token */
1026
1027 if (self->cfg->user_version == 0)
1028 {
1029 msg_error("ERROR: configuration files without a version number has become unsupported in " VERSION_3_13
1030 ", please specify a version number using @version and update your configuration accordingly");
1031 return CLPR_ERROR;
1032 }
1033
1034 cfg_discover_candidate_modules(self->cfg);
1035
1036 cfg_load_forced_modules(self->cfg);
1037
1038 self->non_pragma_seen = TRUE;
1039 }
1040
1041 return CLPR_OK;
1042 }
1043
1044 int
cfg_lexer_lex(CfgLexer * self,CFG_STYPE * yylval,CFG_LTYPE * yylloc)1045 cfg_lexer_lex(CfgLexer *self, CFG_STYPE *yylval, CFG_LTYPE *yylloc)
1046 {
1047 /*
1048 * NOTE:
1049 *
1050 * String tokens are allocated by malloc/free and not
1051 * g_malloc/g_free, this is significant. The grammar contains the free()
1052 * call, so getting rid of that would require a lot of changes to the
1053 * grammar. (on Windows glib, malloc/g_malloc are NOT equivalent)
1054 *
1055 */
1056
1057 gint tok;
1058 gboolean is_token_injected;
1059 CfgLexerPreprocessResult preprocess_result;
1060
1061 do
1062 {
1063 is_token_injected = cfg_lexer_consume_next_injected_token(self, &tok, yylval, yylloc);
1064
1065 if (!is_token_injected)
1066 {
1067 if (cfg_lexer_get_context_type(self) == LL_CONTEXT_BLOCK_CONTENT)
1068 cfg_lexer_start_block_state(self, "{}");
1069 else if (cfg_lexer_get_context_type(self) == LL_CONTEXT_BLOCK_ARG)
1070 cfg_lexer_start_block_state(self, "()");
1071
1072 tok = cfg_lexer_lex_next_token(self, yylval, yylloc);
1073 cfg_lexer_append_preprocessed_output(self, self->token_pretext->str);
1074 }
1075
1076 preprocess_result = cfg_lexer_preprocess(self, tok, yylval, yylloc);
1077 if (preprocess_result == CLPR_ERROR)
1078 return LL_ERROR;
1079 }
1080 while (preprocess_result == CLPR_LEX_AGAIN);
1081
1082 if (!is_token_injected && self->preprocess_suppress_tokens == 0)
1083 cfg_lexer_append_preprocessed_output(self, self->token_text->str);
1084
1085 return tok;
1086 }
1087
1088 static void
cfg_lexer_init(CfgLexer * self,GlobalConfig * cfg)1089 cfg_lexer_init(CfgLexer *self, GlobalConfig *cfg)
1090 {
1091 CfgIncludeLevel *level;
1092
1093 _cfg_lexer_lex_init_extra(self, &self->state);
1094 self->string_buffer = g_string_sized_new(32);
1095 self->token_text = g_string_sized_new(32);
1096 self->token_pretext = g_string_sized_new(32);
1097 self->cfg = cfg;
1098
1099 level = &self->include_stack[0];
1100 level->lloc.first_line = level->lloc.last_line = 1;
1101 level->lloc.first_column = level->lloc.last_column = 1;
1102 level->lloc.level = level;
1103 }
1104
1105
1106 /* NOTE: cfg might be NULL in some call sites, but in those cases the lexer
1107 * should remain operational, obviously skipping cases where it would be
1108 * using the configuration instance. The lexer and the configuration stuff
1109 * should be one-way dependent, right now it is a circular dependency. */
1110 CfgLexer *
cfg_lexer_new(GlobalConfig * cfg,FILE * file,const gchar * filename,GString * preprocess_output)1111 cfg_lexer_new(GlobalConfig *cfg, FILE *file, const gchar *filename, GString *preprocess_output)
1112 {
1113 CfgLexer *self;
1114 CfgIncludeLevel *level;
1115
1116 self = g_new0(CfgLexer, 1);
1117 cfg_lexer_init(self, cfg);
1118 self->preprocess_output = preprocess_output;
1119
1120 level = &self->include_stack[0];
1121 level->include_type = CFGI_FILE;
1122 level->name = g_strdup(filename);
1123 level->yybuf = _cfg_lexer__create_buffer(file, YY_BUF_SIZE, self->state);
1124 _cfg_lexer__switch_to_buffer(level->yybuf, self->state);
1125
1126 return self;
1127 }
1128
1129 CfgLexer *
cfg_lexer_new_buffer(GlobalConfig * cfg,const gchar * buffer,gsize length)1130 cfg_lexer_new_buffer(GlobalConfig *cfg, const gchar *buffer, gsize length)
1131 {
1132 CfgLexer *self;
1133 CfgIncludeLevel *level;
1134
1135 self = g_new0(CfgLexer, 1);
1136 cfg_lexer_init(self, cfg);
1137 self->ignore_pragma = TRUE;
1138
1139 level = &self->include_stack[0];
1140 level->include_type = CFGI_BUFFER;
1141 level->buffer.original_content = g_strdup(buffer);
1142 level->buffer.content = g_malloc(length + 2);
1143 memcpy(level->buffer.content, buffer, length);
1144 level->buffer.content[length] = 0;
1145 level->buffer.content[length + 1] = 0;
1146 level->buffer.content_length = length + 2;
1147 level->name = g_strdup("<string>");
1148 level->yybuf = _cfg_lexer__scan_buffer(level->buffer.content, level->buffer.content_length, self->state);
1149 _cfg_lexer__switch_to_buffer(level->yybuf, self->state);
1150
1151 return self;
1152 }
1153
1154 void
cfg_lexer_free(CfgLexer * self)1155 cfg_lexer_free(CfgLexer *self)
1156 {
1157 gint i;
1158
1159 for (i = 0; i <= self->include_depth; i++)
1160 cfg_lexer_clear_include_level(self, &self->include_stack[i]);
1161
1162 self->include_depth = 0;
1163 _cfg_lexer_lex_destroy(self->state);
1164 g_string_free(self->string_buffer, TRUE);
1165 if (self->token_text)
1166 g_string_free(self->token_text, TRUE);
1167 if (self->token_pretext)
1168 g_string_free(self->token_pretext, TRUE);
1169
1170 while (self->context_stack)
1171 cfg_lexer_pop_context(self);
1172 g_list_foreach(self->token_blocks, (GFunc) cfg_token_block_free, NULL);
1173 g_list_free(self->token_blocks);
1174 g_free(self);
1175 }
1176
1177 static const gchar *lexer_contexts[] =
1178 {
1179 [LL_CONTEXT_ROOT] = "root",
1180 [LL_CONTEXT_DESTINATION] = "destination",
1181 [LL_CONTEXT_SOURCE] = "source",
1182 [LL_CONTEXT_PARSER] = "parser",
1183 [LL_CONTEXT_REWRITE] = "rewrite",
1184 [LL_CONTEXT_FILTER] = "filter",
1185 [LL_CONTEXT_LOG] = "log",
1186 [LL_CONTEXT_BLOCK_DEF] = "block-def",
1187 [LL_CONTEXT_BLOCK_ARG] = "block-arg",
1188 [LL_CONTEXT_BLOCK_REF] = "block-ref",
1189 [LL_CONTEXT_BLOCK_CONTENT] = "block-content",
1190 [LL_CONTEXT_PRAGMA] = "pragma",
1191 [LL_CONTEXT_FORMAT] = "format",
1192 [LL_CONTEXT_TEMPLATE_FUNC] = "template-func",
1193 [LL_CONTEXT_INNER_DEST] = "inner-dest",
1194 [LL_CONTEXT_INNER_SRC] = "inner-src",
1195 [LL_CONTEXT_CLIENT_PROTO] = "client-proto",
1196 [LL_CONTEXT_SERVER_PROTO] = "server-proto",
1197 [LL_CONTEXT_OPTIONS] = "options",
1198 [LL_CONTEXT_CONFIG] = "config",
1199 };
1200
1201 gint
cfg_lexer_lookup_context_type_by_name(const gchar * name)1202 cfg_lexer_lookup_context_type_by_name(const gchar *name)
1203 {
1204 gint i;
1205
1206 for (i = 0; i < G_N_ELEMENTS(lexer_contexts); i++)
1207 {
1208 if (lexer_contexts[i] && strcmp(lexer_contexts[i], name) == 0)
1209 return i;
1210 }
1211 return 0;
1212 }
1213
1214 const gchar *
cfg_lexer_lookup_context_name_by_type(gint type)1215 cfg_lexer_lookup_context_name_by_type(gint type)
1216 {
1217 type &= ~LL_CONTEXT_FLAGS;
1218 g_assert(type < G_N_ELEMENTS(lexer_contexts));
1219 return lexer_contexts[type];
1220 }
1221
1222 /* token blocks */
1223
1224 void
cfg_token_block_add_and_consume_token(CfgTokenBlock * self,CFG_STYPE * token)1225 cfg_token_block_add_and_consume_token(CfgTokenBlock *self, CFG_STYPE *token)
1226 {
1227 g_assert(self->pos == 0);
1228 g_array_append_val(self->tokens, *token);
1229 }
1230
1231 void
cfg_token_block_add_token(CfgTokenBlock * self,CFG_STYPE * token)1232 cfg_token_block_add_token(CfgTokenBlock *self, CFG_STYPE *token)
1233 {
1234 CFG_STYPE copied_token = cfg_lexer_copy_token(token);
1235 cfg_token_block_add_and_consume_token(self, &copied_token);
1236 }
1237
1238 CFG_STYPE *
cfg_token_block_get_token(CfgTokenBlock * self)1239 cfg_token_block_get_token(CfgTokenBlock *self)
1240 {
1241 if (self->pos < self->tokens->len)
1242 {
1243 CFG_STYPE *result;
1244
1245 result = &g_array_index(self->tokens, CFG_STYPE, self->pos);
1246 self->pos++;
1247 return result;
1248 }
1249 return NULL;
1250 }
1251
1252 CfgTokenBlock *
cfg_token_block_new(void)1253 cfg_token_block_new(void)
1254 {
1255 CfgTokenBlock *self = g_new0(CfgTokenBlock, 1);
1256
1257 self->tokens = g_array_new(FALSE, TRUE, sizeof(CFG_STYPE));
1258 return self;
1259 }
1260
1261 void
cfg_token_block_free(CfgTokenBlock * self)1262 cfg_token_block_free(CfgTokenBlock *self)
1263 {
1264 if (self->pos < self->tokens->len)
1265 {
1266 for (gint i = self->pos; i < self->tokens->len; i++)
1267 {
1268 CFG_STYPE *token = &g_array_index(self->tokens, CFG_STYPE, i);
1269
1270 cfg_lexer_free_token(token);
1271 }
1272 }
1273
1274 g_array_free(self->tokens, TRUE);
1275 g_free(self);
1276 }
1277
1278 GQuark
cfg_lexer_error_quark(void)1279 cfg_lexer_error_quark(void)
1280 {
1281 return g_quark_from_static_string("cfg-lexer-error-quark");
1282 }
1283