/
githubmirror
/
rsyslog
Обзор
Документация
Войти
/
githubmirror
/
rsyslog
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
runtime/translate.c
1 966 строк
64 KB
Rainer Gerhards
runtime: fix YAML promotion OOM ownership
04 июн 2026, 19:07
04 июн 2026, 19:07
4c697e3
Код
Авторство
О чём код?
/** @file translate.c * @brief Canonical config translation support for rsyslog. * * Captures config objects and script bodies during config load so rsyslogd * can emit canonical RainerScript or YAML during validation runs. * * Copyright 2026 Rainer Gerhards and Adiscon GmbH. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * -or- * see COPYING.ASL20 in the source distribution * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "config.h" #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "rsyslog.h" #include "translate.h" #include "action.h" #include "errmsg.h" #include "msg.h" #include "grammar/grammar.h" static char *translateVasprintf(const char *fmt, va_list ap); static void cnfarrayCloneDestruct(struct cnfarray *ar); static int preferredKeyRank(const struct nvlst *n); static int nvlstSortComesBefore(const struct nvlst *a, const struct nvlst *b); static int estrAppendCstr(es_str_t **s, const char *buf); static int estrAppendChar(es_str_t **s, char c); static int estrAppendQuoted(es_str_t **s, const char *buf); static int nvlstValueToRs(es_str_t **out, const struct nvlst *node); struct rsconfTranslateWarning_s { char *msg; struct rsconfTranslateWarning_s *next; }; static void addWarning(struct rsconfTranslateWarning_s **head, const char *fmt, ...); struct rsconfTranslateYamlAction_s { struct nvlst *nvlst; struct rsconfTranslateYamlAction_s *next; }; struct rsconfTranslateYamlStatement_s { char *if_expr; struct rsconfTranslateYamlAction_s *actions; struct rsconfTranslateYamlStatement_s *next; }; enum rsconfTranslateYamlRulesetKind_e { RS_TRANSLATE_YAML_RULESET_NONE = 0, RS_TRANSLATE_YAML_RULESET_ACTIONS, RS_TRANSLATE_YAML_RULESET_FILTER_ACTIONS, RS_TRANSLATE_YAML_RULESET_STATEMENTS, }; struct rsconfTranslateItem_s { enum cnfobjType objType; struct nvlst *nvlst; struct objlst *subobjs; char *script; char *source; int line; struct rsconfTranslateWarning_s *warnings; enum rsconfTranslateYamlRulesetKind_e yaml_ruleset_kind; char *yaml_filter; struct rsconfTranslateYamlAction_s *yaml_actions; struct rsconfTranslateYamlStatement_s *yaml_statements; struct rsconfTranslateItem_s *next; }; struct rsconfTranslateState_s { enum rsconfTranslateFormat fmt; int fatal; struct rsconfTranslateWarning_s *globalWarnings; struct rsconfTranslateItem_s *globals; struct rsconfTranslateItem_s *mainqueue; struct rsconfTranslateItem_s *modules; struct rsconfTranslateItem_s *inputs; struct rsconfTranslateItem_s *templates; struct rsconfTranslateItem_s *rulesets; struct rsconfTranslateItem_s *lookups; struct rsconfTranslateItem_s *parsers; struct rsconfTranslateItem_s *timezones; struct rsconfTranslateItem_s *dynstats; struct rsconfTranslateItem_s *perctilestats; struct rsconfTranslateItem_s *ratelimits; }; /* Concurrency & Locking: * Translation capture is only active during single-threaded config load and * validation. g_tx is process-global mutable state with no locking because it * is not accessed concurrently; runtime worker threads are not started while * translation state is being built or emitted. */ static struct rsconfTranslateState_s g_tx = { RSCONF_TRANSLATE_NONE, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; static void cnfarrayCloneDestruct(struct cnfarray *ar) { if (ar == NULL) return; cnfarrayContentDestruct(ar); free(ar); } static void warningsDestruct(struct rsconfTranslateWarning_s *w) { struct rsconfTranslateWarning_s *n; while (w != NULL) { n = w->next; free(w->msg); free(w); w = n; } } static void yamlActionDestruct(struct rsconfTranslateYamlAction_s *act) { while (act != NULL) { struct rsconfTranslateYamlAction_s *n = act->next; nvlstDestruct(act->nvlst); free(act); act = n; } } static void yamlStatementDestruct(struct rsconfTranslateYamlStatement_s *stmt) { while (stmt != NULL) { struct rsconfTranslateYamlStatement_s *n = stmt->next; free(stmt->if_expr); yamlActionDestruct(stmt->actions); free(stmt); stmt = n; } } static void itemDestruct(struct rsconfTranslateItem_s *it) { while (it != NULL) { struct rsconfTranslateItem_s *n = it->next; nvlstDestruct(it->nvlst); objlstDestruct(it->subobjs); free(it->script); free(it->source); warningsDestruct(it->warnings); free(it->yaml_filter); yamlActionDestruct(it->yaml_actions); yamlStatementDestruct(it->yaml_statements); free(it); it = n; } } void rsconfTranslateCleanup(void) { warningsDestruct(g_tx.globalWarnings); itemDestruct(g_tx.globals); itemDestruct(g_tx.mainqueue); itemDestruct(g_tx.modules); itemDestruct(g_tx.inputs); itemDestruct(g_tx.templates); itemDestruct(g_tx.rulesets); itemDestruct(g_tx.lookups); itemDestruct(g_tx.parsers); itemDestruct(g_tx.timezones); itemDestruct(g_tx.dynstats); itemDestruct(g_tx.perctilestats); itemDestruct(g_tx.ratelimits); memset(&g_tx, 0, sizeof(g_tx)); } void rsconfTranslateConfigure(enum rsconfTranslateFormat fmt) { rsconfTranslateCleanup(); g_tx.fmt = fmt; } int rsconfTranslateEnabled(void) { return g_tx.fmt != RSCONF_TRANSLATE_NONE; } int rsconfTranslateHasFatal(void) { return g_tx.fatal; } PRAGMA_DIAGNOSTIC_PUSH PRAGMA_IGNORE_Wformat_nonliteral static char *translateVasprintf(const char *fmt, va_list ap) { va_list ap_copy; char *buf; int len; va_copy(ap_copy, ap); len = vsnprintf(NULL, 0, fmt, ap_copy); va_end(ap_copy); if (len < 0) return NULL; buf = malloc((size_t)len + 1); if (buf == NULL) return NULL; va_copy(ap_copy, ap); if (vsnprintf(buf, (size_t)len + 1, fmt, ap_copy) != len) { va_end(ap_copy); free(buf); return NULL; } va_end(ap_copy); return buf; } PRAGMA_IGNORE_Wformat_nonliteral static void addWarning(struct rsconfTranslateWarning_s **head, const char *fmt, ...) { va_list ap; char *buf; struct rsconfTranslateWarning_s *w; va_start(ap, fmt); buf = translateVasprintf(fmt, ap); va_end(ap); if (buf == NULL) return; w = calloc(1, sizeof(*w)); if (w == NULL) { free(buf); return; } w->msg = buf; if (*head == NULL) { *head = w; } else { struct rsconfTranslateWarning_s *cur = *head; while (cur->next != NULL) cur = cur->next; cur->next = w; } } void rsconfTranslateAddUnsupported(const char *source, int line, const char *fmt, ...) { va_list ap; char *buf; va_start(ap, fmt); buf = translateVasprintf(fmt, ap); va_end(ap); g_tx.fatal = 1; addWarning(&g_tx.globalWarnings, "%s:%d: %s", source == NULL ? "<config>" : source, line, buf == NULL ? "translator: failed to format error message" : buf); free(buf); } PRAGMA_DIAGNOSTIC_POP static es_str_t *cloneEstr(const es_str_t *in) { if (in == NULL) return NULL; return es_newStrFromCStr((char *)es_getBufAddr((es_str_t *)in), es_strlen((es_str_t *)in)); } static struct cnfarray *cloneArray(const struct cnfarray *ar) { int i; struct cnfarray *out; if (ar == NULL) return NULL; out = calloc(1, sizeof(*out)); if (out == NULL) return NULL; out->nodetype = ar->nodetype; out->nmemb = ar->nmemb; if (ar->nmemb > 0) { out->arr = calloc(ar->nmemb, sizeof(es_str_t *)); if (out->arr == NULL) { free(out); return NULL; } for (i = 0; i < ar->nmemb; ++i) { out->arr[i] = cloneEstr(ar->arr[i]); if (out->arr[i] == NULL && ar->arr[i] != NULL) { cnfarrayCloneDestruct(out); return NULL; } } } return out; } static struct nvlst *cloneNvlstNode(const struct nvlst *node) { struct nvlst *out; if (node == NULL) return NULL; out = calloc(1, sizeof(*out)); if (out == NULL) return NULL; out->name = cloneEstr(node->name); if (out->name == NULL && node->name != NULL) { nvlstDestruct(out); return NULL; } out->val.datatype = node->val.datatype; switch (node->val.datatype) { case 'S': out->val.d.estr = cloneEstr(node->val.d.estr); if (out->val.d.estr == NULL && node->val.d.estr != NULL) { nvlstDestruct(out); return NULL; } break; case 'A': out->val.d.ar = cloneArray(node->val.d.ar); if (out->val.d.ar == NULL && node->val.d.ar != NULL) { nvlstDestruct(out); return NULL; } break; case 'N': out->val.d.n = node->val.d.n; break; default: break; } return out; } struct nvlst *rsconfTranslateCloneNvlst(const struct nvlst *lst) { struct nvlst *head = NULL, *n; while (lst != NULL) { struct nvlst *prev = NULL, *cur = head; n = cloneNvlstNode(lst); if (n == NULL) { nvlstDestruct(head); return NULL; } while (cur != NULL && !nvlstSortComesBefore(n, cur)) { prev = cur; cur = cur->next; } n->next = cur; if (prev == NULL) { head = n; } else { prev->next = n; } lst = lst->next; } return head; } static struct objlst *cloneObjlst(const struct objlst *lst) { struct objlst *head = NULL, *tail = NULL, *n; while (lst != NULL) { struct cnfobj *obj = calloc(1, sizeof(*obj)); if (obj == NULL) { objlstDestruct(head); return NULL; } obj->objType = lst->obj->objType; obj->nvlst = rsconfTranslateCloneNvlst(lst->obj->nvlst); if (obj->nvlst == NULL && lst->obj->nvlst != NULL) { cnfobjDestruct(obj); objlstDestruct(head); return NULL; } n = objlstNew(obj); if (n == NULL) { cnfobjDestruct(obj); objlstDestruct(head); return NULL; } if (head == NULL) { head = tail = n; } else { tail->next = n; tail = n; } lst = lst->next; } return head; } static int stmtListIsStructuredActionsOnly(const struct cnfstmt *stmt) { while (stmt != NULL) { if (stmt->nodetype != S_ACT) return 0; if (stmt->d.act != NULL && stmt->d.act->pSyntaxLst != NULL) { stmt = stmt->next; continue; } if (stmt->printable != NULL) { const char *s = (const char *)stmt->printable; while (*s == ' ' || *s == '\t') ++s; if (*s == '/' || (*s == '-' && s[1] == '/') || !strncmp(s, ":omusrmsg:", 10)) { stmt = stmt->next; continue; } } return 0; } return 1; } static struct nvlst *newStringNode(const char *name, const char *value) { es_str_t *nm = NULL; es_str_t *val = NULL; struct nvlst *node = NULL; nm = es_newStrFromCStr(name, strlen(name)); if (nm == NULL) goto done; val = es_newStrFromCStr(value, strlen(value)); if (val == NULL) { es_deleteStr(nm); goto done; } node = nvlstNewStr(val); if (node == NULL) { es_deleteStr(val); es_deleteStr(nm); goto done; } node = nvlstSetName(node, nm); if (node == NULL) { es_deleteStr(val); es_deleteStr(nm); } done: return node; } static void yamlActionNodeAppend(struct nvlst **head, struct nvlst *node) { struct nvlst *cur; if (node == NULL) return; node->next = NULL; if (*head == NULL) { *head = node; return; } cur = *head; while (cur->next != NULL) cur = cur->next; cur->next = node; } static struct nvlst *legacyActionToNvlst(const char *printable, int *oom) { const char *s = printable; struct nvlst *lst = NULL; struct nvlst *node; if (printable == NULL) return NULL; while (*s == ' ' || *s == '\t') ++s; if (*s == '/' || (*s == '-' && s[1] == '/')) { if (*s == '-') ++s; node = newStringNode("type", "omfile"); if (node == NULL) goto oom_fail; yamlActionNodeAppend(&lst, node); node = newStringNode("file", s); if (node == NULL) goto oom_fail; yamlActionNodeAppend(&lst, node); return lst; } if (!strncmp(s, ":omusrmsg:", 10)) { node = newStringNode("type", "omusrmsg"); if (node == NULL) goto oom_fail; yamlActionNodeAppend(&lst, node); node = newStringNode("users", s + 10); if (node == NULL) goto oom_fail; yamlActionNodeAppend(&lst, node); return lst; } return NULL; oom_fail: *oom = 1; nvlstDestruct(lst); return NULL; } static char *buildPriFilterExpr(const char *selector) { es_str_t *estr; char *out; const char *p; int use_single = 1; estr = es_newStr(64); if (estr == NULL) return NULL; for (p = selector; *p != '\0'; ++p) { if (*p == '\'') { use_single = 0; break; } } if (use_single) { if (estrAppendCstr(&estr, "prifilt('") != 0) { es_deleteStr(estr); return NULL; } } else if (estrAppendCstr(&estr, "prifilt(") != 0 || estrAppendQuoted(&estr, selector) != 0 || estrAppendChar(&estr, ')') != 0) { es_deleteStr(estr); return NULL; } if (!use_single) { out = es_str2cstr(estr, NULL); es_deleteStr(estr); return out; } for (p = selector; *p != '\0'; ++p) { if (estrAppendChar(&estr, *p) != 0) { es_deleteStr(estr); return NULL; } } if (estrAppendCstr(&estr, "')") != 0) { es_deleteStr(estr); return NULL; } out = es_str2cstr(estr, NULL); es_deleteStr(estr); return out; } static int appendActionNvlstSingleline(es_str_t **out, const struct nvlst *lst) { const struct nvlst *n; char *name; int first = 1; int rank; if (estrAppendCstr(out, "action(") != 0) return -1; for (rank = 0; rank < 4; ++rank) { for (n = lst; n != NULL; n = n->next) { if (preferredKeyRank(n) != rank) continue; name = es_str2cstr(n->name, NULL); if (name == NULL) return -1; if (!first && estrAppendCstr(out, " ") != 0) { free(name); return -1; } first = 0; if (estrAppendCstr(out, name) != 0 || estrAppendChar(out, '=') != 0 || nvlstValueToRs(out, n) != 0) { free(name); return -1; } free(name); } } return estrAppendChar(out, ')'); } static struct rsconfTranslateYamlAction_s *cloneYamlActions(const struct cnfstmt *stmt, int *oom) { struct rsconfTranslateYamlAction_s *head = NULL, *tail = NULL, *node; while (stmt != NULL) { int legacy_oom = 0; node = calloc(1, sizeof(*node)); if (node == NULL) { *oom = 1; yamlActionDestruct(head); return NULL; } if (stmt->d.act != NULL && stmt->d.act->pSyntaxLst != NULL) { node->nvlst = rsconfTranslateCloneNvlst(stmt->d.act->pSyntaxLst); } else { node->nvlst = legacyActionToNvlst((const char *)stmt->printable, &legacy_oom); } if (node->nvlst == NULL) { *oom = legacy_oom; free(node); yamlActionDestruct(head); return NULL; } if (head == NULL) { head = tail = node; } else { tail->next = node; tail = node; } stmt = stmt->next; } return head; } static void yamlActionsAppend(struct rsconfTranslateYamlAction_s **dst, struct rsconfTranslateYamlAction_s *src) { struct rsconfTranslateYamlAction_s *tail; if (*dst == NULL) { *dst = src; return; } tail = *dst; while (tail->next != NULL) tail = tail->next; tail->next = src; } static struct rsconfTranslateYamlStatement_s *cloneYamlLegacyStatements(const struct cnfstmt *stmt, int *oom) { struct rsconfTranslateYamlStatement_s *head = NULL, *tail = NULL, *node; while (stmt != NULL) { const struct cnfstmt *then_branch = NULL; char *if_expr; if (stmt->nodetype != S_PRIFILT || stmt->d.s_prifilt.t_else != NULL || stmt->printable == NULL) { yamlStatementDestruct(head); return NULL; } then_branch = stmt->d.s_prifilt.t_then; if (!stmtListIsStructuredActionsOnly(then_branch)) { yamlStatementDestruct(head); return NULL; } if_expr = buildPriFilterExpr((const char *)stmt->printable); if (if_expr == NULL) { *oom = 1; yamlStatementDestruct(head); return NULL; } node = calloc(1, sizeof(*node)); if (node == NULL) { free(if_expr); *oom = 1; yamlStatementDestruct(head); return NULL; } node->if_expr = if_expr; node->actions = cloneYamlActions(then_branch, oom); if (node->actions == NULL) { yamlStatementDestruct(node); yamlStatementDestruct(head); return NULL; } if (head == NULL) { head = tail = node; } else { tail->next = node; tail = node; } stmt = stmt->next; } return head; } static struct rsconfTranslateYamlStatement_s *newYamlStatement(char *if_expr, struct rsconfTranslateYamlAction_s *actions, int *oom) { struct rsconfTranslateYamlStatement_s *node = calloc(1, sizeof(*node)); if (node == NULL) { *oom = 1; free(if_expr); yamlActionDestruct(actions); return NULL; } node->if_expr = if_expr; node->actions = actions; return node; } static void yamlStatementsAppend(struct rsconfTranslateYamlStatement_s **dst, struct rsconfTranslateYamlStatement_s *src) { struct rsconfTranslateYamlStatement_s *tail; if (*dst == NULL) { *dst = src; return; } tail = *dst; while (tail->next != NULL) tail = tail->next; tail->next = src; } static int promoteSimpleYamlToStatements(struct rsconfTranslateItem_s *it, int *oom) { char *if_expr; struct rsconfTranslateYamlAction_s *actions; struct rsconfTranslateYamlStatement_s *stmt; if (it->yaml_ruleset_kind == RS_TRANSLATE_YAML_RULESET_STATEMENTS) return 1; if (it->yaml_ruleset_kind != RS_TRANSLATE_YAML_RULESET_FILTER_ACTIONS || it->yaml_filter == NULL || it->yaml_actions == NULL) return 0; if_expr = buildPriFilterExpr(it->yaml_filter); if (if_expr == NULL) { *oom = 1; return 0; } actions = it->yaml_actions; it->yaml_actions = NULL; stmt = newYamlStatement(if_expr, actions, oom); if (stmt == NULL) return 0; free(it->yaml_filter); it->yaml_filter = NULL; it->yaml_statements = stmt; it->yaml_ruleset_kind = RS_TRANSLATE_YAML_RULESET_STATEMENTS; return 1; } static int appendYamlLegacyStatement(struct rsconfTranslateItem_s *it, const struct cnfstmt *script, int *oom) { const struct cnfstmt *then_branch = NULL; char *if_expr; struct rsconfTranslateYamlAction_s *actions; struct rsconfTranslateYamlStatement_s *stmt; if (script == NULL || script->next != NULL || script->nodetype != S_PRIFILT || script->d.s_prifilt.t_else != NULL || script->printable == NULL) return 0; then_branch = script->d.s_prifilt.t_then; if (!stmtListIsStructuredActionsOnly(then_branch)) return 0; if (!promoteSimpleYamlToStatements(it, oom) || it->yaml_ruleset_kind != RS_TRANSLATE_YAML_RULESET_STATEMENTS) { return 0; } if_expr = buildPriFilterExpr((const char *)script->printable); if (if_expr == NULL) { *oom = 1; return 0; } actions = cloneYamlActions(then_branch, oom); if (actions == NULL) { free(if_expr); return 0; } stmt = newYamlStatement(if_expr, actions, oom); if (stmt == NULL) return 0; yamlStatementsAppend(&it->yaml_statements, stmt); return 1; } static void captureYamlRulesetBody(struct rsconfTranslateItem_s *it, const struct cnfstmt *script) { int oom = 0; if (it == NULL || script == NULL) return; if (stmtListIsStructuredActionsOnly(script)) { struct rsconfTranslateYamlAction_s *actions = cloneYamlActions(script, &oom); if (oom) { addWarning(&it->warnings, "translator: out of memory when capturing YAML action list"); g_tx.fatal = 1; return; } if (actions == NULL) { /* Unsupported legacy action shorthand, fall back below. */ } else if (it->yaml_ruleset_kind == RS_TRANSLATE_YAML_RULESET_NONE) { yamlStatementDestruct(it->yaml_statements); it->yaml_statements = NULL; it->yaml_actions = actions; it->yaml_ruleset_kind = RS_TRANSLATE_YAML_RULESET_ACTIONS; return; } else if (it->yaml_ruleset_kind == RS_TRANSLATE_YAML_RULESET_ACTIONS && it->yaml_filter == NULL) { yamlActionsAppend(&it->yaml_actions, actions); return; } else { yamlActionDestruct(actions); free(it->yaml_filter); it->yaml_filter = NULL; yamlActionDestruct(it->yaml_actions); it->yaml_actions = NULL; yamlStatementDestruct(it->yaml_statements); it->yaml_statements = NULL; it->yaml_ruleset_kind = RS_TRANSLATE_YAML_RULESET_NONE; } } if (script->next == NULL && script->printable != NULL) { const struct cnfstmt *then_branch = NULL; if (script->nodetype == S_PRIFILT && script->d.s_prifilt.t_else == NULL) { then_branch = script->d.s_prifilt.t_then; } else if (script->nodetype == S_PROPFILT && script->d.s_propfilt.t_else == NULL) { then_branch = script->d.s_propfilt.t_then; } if (then_branch != NULL && stmtListIsStructuredActionsOnly(then_branch) && it->yaml_ruleset_kind == RS_TRANSLATE_YAML_RULESET_NONE) { it->yaml_filter = strdup((char *)script->printable); if (it->yaml_filter == NULL) { addWarning(&it->warnings, "translator: out of memory when capturing YAML filter"); g_tx.fatal = 1; return; } it->yaml_actions = cloneYamlActions(then_branch, &oom); if (oom) { addWarning(&it->warnings, "translator: out of memory when capturing YAML action list"); g_tx.fatal = 1; return; } if (it->yaml_actions != NULL) { yamlStatementDestruct(it->yaml_statements); it->yaml_statements = NULL; it->yaml_ruleset_kind = RS_TRANSLATE_YAML_RULESET_FILTER_ACTIONS; return; } free(it->yaml_filter); it->yaml_filter = NULL; } } if (it->yaml_ruleset_kind == RS_TRANSLATE_YAML_RULESET_NONE) { it->yaml_statements = cloneYamlLegacyStatements(script, &oom); if (oom) { addWarning(&it->warnings, "translator: out of memory when capturing YAML statements"); g_tx.fatal = 1; return; } if (it->yaml_statements != NULL) { it->yaml_ruleset_kind = RS_TRANSLATE_YAML_RULESET_STATEMENTS; return; } } free(it->yaml_filter); it->yaml_filter = NULL; yamlActionDestruct(it->yaml_actions); it->yaml_actions = NULL; yamlStatementDestruct(it->yaml_statements); it->yaml_statements = NULL; it->yaml_ruleset_kind = RS_TRANSLATE_YAML_RULESET_NONE; } static int stmtListIsSelectorCompatible(const struct cnfstmt *stmt) { while (stmt != NULL) { switch (stmt->nodetype) { case S_ACT: case S_STOP: case S_CALL: case S_CALL_INDIRECT: case S_NOP: break; default: return 0; } stmt = stmt->next; } return 1; } static int estrAppendCstr(es_str_t **s, const char *buf) { return es_addBuf(s, buf, strlen(buf)); } static int estrAppendChar(es_str_t **s, char c) { return es_addChar(s, (unsigned char)c); } static int estrAppendVarName(es_str_t **s, const char *name) { if (name == NULL || name[0] == '\0') return -1; if (name[0] != '$' && estrAppendChar(s, '$') != 0) return -1; return estrAppendCstr(s, name); } static int estrAppendQuoted(es_str_t **s, const char *buf) { size_t i; if (estrAppendChar(s, '"') != 0) return -1; for (i = 0; buf[i] != '\0'; ++i) { switch (buf[i]) { case '"': case '\\': if (estrAppendChar(s, '\\') != 0 || estrAppendChar(s, buf[i]) != 0) return -1; break; case '\n': if (estrAppendCstr(s, "\\n") != 0) return -1; break; case '\r': if (estrAppendCstr(s, "\\r") != 0) return -1; break; case '\t': if (estrAppendCstr(s, "\\t") != 0) return -1; break; default: if (estrAppendChar(s, buf[i]) != 0) return -1; break; } } return estrAppendChar(s, '"'); } static const char *exprOpToStr(unsigned nodetype) { switch (nodetype) { case CMP_EQ: return "=="; case CMP_NE: return "!="; case CMP_LE: return "<="; case CMP_GE: return ">="; case CMP_LT: return "<"; case CMP_GT: return ">"; case CMP_CONTAINS: return "contains"; case CMP_CONTAINSI: return "contains_i"; case CMP_STARTSWITH: return "startswith"; case CMP_STARTSWITHI: return "startswith_i"; case CMP_ENDSWITH: return "endswith"; case OR: return "or"; case AND: return "and"; default: return NULL; } } static int exprToString(es_str_t **out, const struct cnfexpr *expr, struct rsconfTranslateWarning_s **warnings); static int exprListToString(es_str_t **out, unsigned short nParams, const struct cnfexpr *const *exprs, struct rsconfTranslateWarning_s **warnings) { unsigned short i; for (i = 0; i < nParams; ++i) { if (i > 0 && estrAppendCstr(out, ", ") != 0) return -1; if (exprToString(out, exprs[i], warnings) != 0) return -1; } return 0; } static int exprToString(es_str_t **out, const struct cnfexpr *expr, struct rsconfTranslateWarning_s **warnings) { const char *op; char nbuf[64]; int i; char *cstr; const struct cnffunc *func; const struct cnfarray *ar; if (expr == NULL) return -1; op = exprOpToStr(expr->nodetype); if (op != NULL) { if (estrAppendChar(out, '(') != 0) return -1; if (exprToString(out, expr->l, warnings) != 0) return -1; if (estrAppendCstr(out, " ") != 0 || estrAppendCstr(out, op) != 0 || estrAppendCstr(out, " ") != 0) return -1; if (exprToString(out, expr->r, warnings) != 0) return -1; return estrAppendChar(out, ')'); } switch (expr->nodetype) { case NOT: if (estrAppendCstr(out, "not ") != 0) return -1; return exprToString(out, expr->r, warnings); case 'N': { int len = snprintf(nbuf, sizeof(nbuf), "%lld", ((const struct cnfnumval *)expr)->val); if (len < 0 || len >= (int)sizeof(nbuf)) return -1; return estrAppendCstr(out, nbuf); } case 'S': cstr = es_str2cstr(((const struct cnfstringval *)expr)->estr, NULL); if (cstr == NULL) return -1; i = estrAppendQuoted(out, cstr); free(cstr); return i; case 'V': return estrAppendVarName(out, ((const struct cnfvar *)expr)->name); case 'A': ar = (const struct cnfarray *)expr; if (estrAppendChar(out, '[') != 0) return -1; for (i = 0; i < ar->nmemb; ++i) { char *aval = es_str2cstr(ar->arr[i], NULL); if (i > 0 && estrAppendCstr(out, ", ") != 0) { free(aval); return -1; } if (aval == NULL || estrAppendQuoted(out, aval) != 0) { free(aval); return -1; } free(aval); } return estrAppendChar(out, ']'); case 'F': func = (const struct cnffunc *)expr; cstr = es_str2cstr(func->fname, NULL); if (cstr == NULL) return -1; if (estrAppendCstr(out, cstr) != 0 || estrAppendChar(out, '(') != 0) { free(cstr); return -1; } free(cstr); if (exprListToString(out, func->nParams, (const struct cnfexpr *const *)func->expr, warnings) != 0 || estrAppendChar(out, ')') != 0) return -1; return 0; case S_FUNC_EXISTS: if (estrAppendCstr(out, "exists(") != 0) return -1; if (estrAppendVarName(out, ((const struct cnffuncexists *)expr)->varname) != 0) return -1; return estrAppendChar(out, ')'); case '&': case '+': case '-': case '*': case '/': case '%': case 'M': if (expr->nodetype == 'M') { if (estrAppendChar(out, '-') != 0) return -1; return exprToString(out, expr->r, warnings); } if (estrAppendChar(out, '(') != 0) return -1; if (exprToString(out, expr->l, warnings) != 0) return -1; if (estrAppendCstr(out, " ") != 0 || estrAppendChar(out, (char)expr->nodetype) != 0 || estrAppendCstr(out, " ") != 0) return -1; if (exprToString(out, expr->r, warnings) != 0) return -1; return estrAppendChar(out, ')'); default: addWarning(warnings, "translator: unsupported expression node %u", expr->nodetype); return -1; } } static int nvlstValueToRs(es_str_t **out, const struct nvlst *node) { char *cstr; int i; struct cnfarray *ar; char nbuf[64]; switch (node->val.datatype) { case 'S': cstr = es_str2cstr(node->val.d.estr, NULL); if (cstr == NULL) return -1; i = estrAppendQuoted(out, cstr); free(cstr); return i; case 'A': ar = node->val.d.ar; if (estrAppendChar(out, '[') != 0) return -1; for (i = 0; i < ar->nmemb; ++i) { cstr = es_str2cstr(ar->arr[i], NULL); if (cstr == NULL) return -1; if ((i > 0 && estrAppendCstr(out, ", ") != 0) || estrAppendQuoted(out, cstr) != 0) { free(cstr); return -1; } free(cstr); } return estrAppendChar(out, ']'); case 'N': { int len = snprintf(nbuf, sizeof(nbuf), "%lld", node->val.d.n); if (len < 0 || len >= (int)sizeof(nbuf)) return -1; return estrAppendCstr(out, nbuf); } default: return -1; } } static int emitActionSingleline(es_str_t **out, const struct cnfstmt *stmt, struct rsconfTranslateWarning_s **warnings) { char *name; switch (stmt->nodetype) { case S_ACT: if (stmt->d.act != NULL && stmt->d.act->pSyntaxLst != NULL) { return appendActionNvlstSingleline(out, stmt->d.act->pSyntaxLst); } if (stmt->printable != NULL) { int oom = 0; struct nvlst *legacy = legacyActionToNvlst((char *)stmt->printable, &oom); if (legacy != NULL) { int ret = appendActionNvlstSingleline(out, legacy); nvlstDestruct(legacy); return ret; } if (oom) { addWarning(warnings, "translator: out of memory when converting legacy action syntax"); return -1; } addWarning(warnings, "legacy action syntax preserved as script text"); return estrAppendCstr(out, (char *)stmt->printable); } addWarning(warnings, "translator: action statement lost original syntax"); return -1; case S_STOP: return estrAppendCstr(out, "stop"); case S_CALL: { int ret; if (estrAppendCstr(out, "call ") != 0) return -1; name = es_str2cstr(stmt->d.s_call.name, NULL); if (name == NULL) return -1; ret = estrAppendCstr(out, name); free(name); return ret; } case S_CALL_INDIRECT: if (estrAppendCstr(out, "call_indirect ") != 0) return -1; if (exprToString(out, stmt->d.s_call_ind.expr, warnings) != 0) return -1; return estrAppendChar(out, ';'); case S_NOP: if (stmt->printable != NULL && !strcmp((char *)stmt->printable, "continue")) { return estrAppendCstr(out, "continue"); } addWarning(warnings, "translator: omitted internal NOP statement"); return -1; default: return -1; } } static int stmtListToString(es_str_t **out, const struct cnfstmt *stmt, int indent, struct rsconfTranslateWarning_s **warnings); static int appendIndent(es_str_t **out, int indent) { while (indent-- > 0) { if (estrAppendCstr(out, " ") != 0) return -1; } return 0; } static int emitSelectorBlock(es_str_t **out, const struct cnfstmt *stmt, int indent, const char *selector, struct rsconfTranslateWarning_s **warnings) { const struct cnfstmt *cur = stmt; int first = 1; while (cur != NULL) { if (appendIndent(out, indent) != 0) return -1; if (!first && estrAppendCstr(out, "& ") != 0) return -1; if (first) { if (estrAppendCstr(out, selector) != 0 || estrAppendCstr(out, " ") != 0) return -1; } first = 0; if (emitActionSingleline(out, cur, warnings) != 0 || estrAppendChar(out, '\n') != 0) return -1; cur = cur->next; } return 0; } static int stmtListToString(es_str_t **out, const struct cnfstmt *stmt, int indent, struct rsconfTranslateWarning_s **warnings) { while (stmt != NULL) { switch (stmt->nodetype) { case S_ACT: case S_STOP: case S_CALL: case S_CALL_INDIRECT: case S_NOP: if (appendIndent(out, indent) != 0 || emitActionSingleline(out, stmt, warnings) != 0 || estrAppendChar(out, '\n') != 0) return -1; break; case S_SET: if (appendIndent(out, indent) != 0) return -1; if (estrAppendCstr(out, stmt->d.s_set.force_reset ? "reset " : "set ") != 0 || estrAppendVarName(out, (char *)stmt->d.s_set.varname) != 0 || estrAppendCstr(out, " = ") != 0 || exprToString(out, stmt->d.s_set.expr, warnings) != 0 || estrAppendCstr(out, ";\n") != 0) return -1; break; case S_UNSET: if (appendIndent(out, indent) != 0 || estrAppendCstr(out, "unset ") != 0 || estrAppendVarName(out, (char *)stmt->d.s_unset.varname) != 0 || estrAppendCstr(out, ";\n") != 0) return -1; break; case S_IF: if (appendIndent(out, indent) != 0 || estrAppendCstr(out, "if ") != 0 || exprToString(out, stmt->d.s_if.expr, warnings) != 0 || estrAppendCstr(out, " then {\n") != 0 || stmtListToString(out, stmt->d.s_if.t_then, indent + 1, warnings) != 0) return -1; if (appendIndent(out, indent) != 0) return -1; if (stmt->d.s_if.t_else != NULL) { if (estrAppendCstr(out, "} else {\n") != 0 || stmtListToString(out, stmt->d.s_if.t_else, indent + 1, warnings) != 0 || appendIndent(out, indent) != 0 || estrAppendCstr(out, "}\n") != 0) return -1; } else if (estrAppendCstr(out, "}\n") != 0) { return -1; } break; case S_FOREACH: if (appendIndent(out, indent) != 0 || estrAppendCstr(out, "foreach (") != 0 || estrAppendVarName(out, stmt->d.s_foreach.iter->var) != 0 || estrAppendCstr(out, " in ") != 0 || exprToString(out, stmt->d.s_foreach.iter->collection, warnings) != 0 || estrAppendCstr(out, ") do {\n") != 0 || stmtListToString(out, stmt->d.s_foreach.body, indent + 1, warnings) != 0 || appendIndent(out, indent) != 0 || estrAppendCstr(out, "}\n") != 0) return -1; break; case S_PRIFILT: if (stmt->d.s_prifilt.t_else == NULL && stmtListIsSelectorCompatible(stmt->d.s_prifilt.t_then)) { if (emitSelectorBlock(out, stmt->d.s_prifilt.t_then, indent, (char *)stmt->printable, warnings) != 0) return -1; } else { addWarning(warnings, "PRI selector normalized into if/prifilt() form"); if (appendIndent(out, indent) != 0 || estrAppendCstr(out, "if prifilt(") != 0 || estrAppendQuoted(out, (char *)stmt->printable) != 0 || estrAppendCstr(out, ") then {\n") != 0 || stmtListToString(out, stmt->d.s_prifilt.t_then, indent + 1, warnings) != 0 || appendIndent(out, indent) != 0) return -1; if (stmt->d.s_prifilt.t_else != NULL) { if (estrAppendCstr(out, "} else {\n") != 0 || stmtListToString(out, stmt->d.s_prifilt.t_else, indent + 1, warnings) != 0 || appendIndent(out, indent) != 0 || estrAppendCstr(out, "}\n") != 0) return -1; } else if (estrAppendCstr(out, "}\n") != 0) { return -1; } } break; case S_PROPFILT: if (stmt->d.s_propfilt.t_else == NULL && stmtListIsSelectorCompatible(stmt->d.s_propfilt.t_then)) { if (emitSelectorBlock(out, stmt->d.s_propfilt.t_then, indent, (char *)stmt->printable, warnings) != 0) return -1; } else { addWarning(warnings, "property selector with else/complex body is not safely translatable"); return -1; } break; case S_RELOAD_LOOKUP_TABLE: if (appendIndent(out, indent) != 0 || estrAppendCstr(out, "reload_lookup_table(") != 0 || estrAppendQuoted(out, (char *)stmt->d.s_reload_lookup_table.table_name) != 0 || estrAppendCstr(out, ", stub_value=") != 0 || estrAppendQuoted(out, (char *)stmt->d.s_reload_lookup_table.stub_value) != 0 || estrAppendCstr(out, ")\n") != 0) return -1; break; default: addWarning(warnings, "translator: unsupported statement node %u", stmt->nodetype); return -1; } stmt = stmt->next; } return 0; } static struct rsconfTranslateItem_s **selectList(enum cnfobjType t) { switch (t) { case CNFOBJ_GLOBAL: return &g_tx.globals; case CNFOBJ_MAINQ: return &g_tx.mainqueue; case CNFOBJ_MODULE: return &g_tx.modules; case CNFOBJ_INPUT: return &g_tx.inputs; case CNFOBJ_TPL: return &g_tx.templates; case CNFOBJ_RULESET: return &g_tx.rulesets; case CNFOBJ_LOOKUP_TABLE: return &g_tx.lookups; case CNFOBJ_PARSER: return &g_tx.parsers; case CNFOBJ_TIMEZONE: return &g_tx.timezones; case CNFOBJ_DYN_STATS: return &g_tx.dynstats; case CNFOBJ_PERCTILE_STATS: return &g_tx.perctilestats; case CNFOBJ_RATELIMIT: return &g_tx.ratelimits; case CNFOBJ_ACTION: case CNFOBJ_PROPERTY: case CNFOBJ_CONSTANT: default: return NULL; } } static void mergeSingleton(struct rsconfTranslateItem_s *dst, const struct nvlst *lst) { const struct nvlst *src; for (src = lst; src != NULL; src = src->next) { struct nvlst *prev = NULL; struct nvlst *cur = dst->nvlst; while (cur != NULL) { char *lhs = es_str2cstr(cur->name, NULL); char *rhs = es_str2cstr(src->name, NULL); int eq = lhs != NULL && rhs != NULL && !strcasecmp(lhs, rhs); free(lhs); free(rhs); if (eq) break; prev = cur; cur = cur->next; } if (cur != NULL) { struct nvlst *rep = cloneNvlstNode(src); if (rep == NULL) continue; rep->next = cur->next; if (prev == NULL) dst->nvlst = rep; else prev->next = rep; cur->next = NULL; nvlstDestruct(cur); } else { struct nvlst *rep = cloneNvlstNode(src); struct nvlst *tail; if (rep == NULL) continue; rep->next = NULL; if (dst->nvlst == NULL) { dst->nvlst = rep; } else { tail = dst->nvlst; while (tail->next != NULL) tail = tail->next; tail->next = rep; } } } } void rsconfTranslateCaptureObj(const struct cnfobj *o, const char *source, int line) { struct rsconfTranslateItem_s **list; struct rsconfTranslateItem_s *it; es_str_t *estr; if (!rsconfTranslateEnabled() || o == NULL) return; list = selectList(o->objType); if (list == NULL) return; if ((o->objType == CNFOBJ_GLOBAL || o->objType == CNFOBJ_MAINQ) && *list != NULL) { mergeSingleton(*list, o->nvlst); return; } it = calloc(1, sizeof(*it)); if (it == NULL) { g_tx.fatal = 1; return; } it->objType = o->objType; it->nvlst = rsconfTranslateCloneNvlst(o->nvlst); if (it->nvlst == NULL && o->nvlst != NULL) { g_tx.fatal = 1; itemDestruct(it); return; } it->subobjs = cloneObjlst(o->subobjs); if (it->subobjs == NULL && o->subobjs != NULL) { g_tx.fatal = 1; itemDestruct(it); return; } it->source = source ? strdup(source) : NULL; if (source != NULL && it->source == NULL) { g_tx.fatal = 1; itemDestruct(it); return; } it->line = line; if (o->objType == CNFOBJ_RULESET && o->script != NULL) { estr = es_newStr(256); if (estr == NULL || stmtListToString(&estr, o->script, 1, &it->warnings) != 0) { addWarning(&it->warnings, "translator: failed to serialize ruleset body"); g_tx.fatal = 1; if (estr != NULL) es_deleteStr(estr); } else { it->script = es_str2cstr(estr, NULL); es_deleteStr(estr); if (it->script == NULL) { addWarning(&it->warnings, "translator: out of memory when capturing ruleset body"); g_tx.fatal = 1; } } captureYamlRulesetBody(it, o->script); } if (*list == NULL) { *list = it; } else { struct rsconfTranslateItem_s *tail = *list; while (tail->next != NULL) tail = tail->next; tail->next = it; } } void rsconfTranslateCaptureScript(const struct cnfstmt *script, const char *source, int line) { struct rsconfTranslateItem_s *it = g_tx.rulesets; es_str_t *estr; struct nvlst *nameNode; int had_script; if (!rsconfTranslateEnabled() || script == NULL) return; while (it != NULL) { struct nvlst *cur; for (cur = it->nvlst; cur != NULL; cur = cur->next) { char *k = es_str2cstr(cur->name, NULL); int match = k != NULL && !strcasecmp(k, "name"); free(k); if (match) break; } if (cur != NULL) { char *v = es_str2cstr(cur->val.d.estr, NULL); int match = v != NULL && !strcmp(v, "RSYSLOG_DefaultRuleset"); free(v); if (match) break; } it = it->next; } if (it == NULL) { es_str_t *nm = es_newStrFromCStr("name", 4); es_str_t *val = es_newStrFromCStr("RSYSLOG_DefaultRuleset", strlen("RSYSLOG_DefaultRuleset")); if (nm == NULL || val == NULL) { es_deleteStr(nm); es_deleteStr(val); g_tx.fatal = 1; return; } nameNode = nvlstNewStr(val); if (nameNode == NULL) { es_deleteStr(val); es_deleteStr(nm); g_tx.fatal = 1; return; } nameNode = nvlstSetName(nameNode, nm); it = calloc(1, sizeof(*it)); if (it == NULL) { nvlstDestruct(nameNode); g_tx.fatal = 1; return; } it->objType = CNFOBJ_RULESET; it->nvlst = nameNode; it->source = source ? strdup(source) : NULL; it->line = line; addWarning(&it->warnings, "top-level statements normalized into explicit RSYSLOG_DefaultRuleset"); if (g_tx.rulesets == NULL) { g_tx.rulesets = it; } else { struct rsconfTranslateItem_s *tail = g_tx.rulesets; while (tail->next != NULL) tail = tail->next; tail->next = it; } } had_script = it->script != NULL; estr = es_newStr(256); if (estr == NULL || stmtListToString(&estr, script, 1, &it->warnings) != 0) { addWarning(&it->warnings, "translator: failed to serialize top-level script"); g_tx.fatal = 1; if (estr != NULL) es_deleteStr(estr); return; } if (it->script == NULL) { it->script = es_str2cstr(estr, NULL); if (it->script == NULL) { addWarning(&it->warnings, "translator: out of memory when capturing script"); g_tx.fatal = 1; } } else { char *more = es_str2cstr(estr, NULL); if (more == NULL) { addWarning(&it->warnings, "translator: out of memory when capturing script"); g_tx.fatal = 1; es_deleteStr(estr); return; } size_t oldlen = strlen(it->script); size_t addlen = strlen(more); char *merged = realloc(it->script, oldlen + addlen + 1); if (merged == NULL) { addWarning(&it->warnings, "translator: out of memory when merging script parts"); g_tx.fatal = 1; } else { memcpy(merged + oldlen, more, addlen + 1); it->script = merged; } free(more); } es_deleteStr(estr); if (had_script) { int oom = 0; if (!appendYamlLegacyStatement(it, script, &oom)) { if (oom) { addWarning(&it->warnings, "translator: out of memory when capturing YAML statements"); g_tx.fatal = 1; } else { free(it->yaml_filter); it->yaml_filter = NULL; yamlActionDestruct(it->yaml_actions); it->yaml_actions = NULL; yamlStatementDestruct(it->yaml_statements); it->yaml_statements = NULL; it->yaml_ruleset_kind = RS_TRANSLATE_YAML_RULESET_NONE; } } } else { captureYamlRulesetBody(it, script); } } static void writeWarningComments(FILE *fp, const struct rsconfTranslateWarning_s *w, int indent) { while (w != NULL) { int i; for (i = 0; i < indent; ++i) fputs(" ", fp); fprintf(fp, "# TRANSLATION WARNING: %s\n", w->msg); w = w->next; } } static void logWarnings(const struct rsconfTranslateWarning_s *w) { while (w != NULL) { LogError(0, RS_RET_CONF_PARSE_ERROR, "%s", w->msg); w = w->next; } } static void logItemWarnings(const struct rsconfTranslateItem_s *items) { while (items != NULL) { logWarnings(items->warnings); items = items->next; } } static void logAllWarnings(void) { logWarnings(g_tx.globalWarnings); logItemWarnings(g_tx.globals); logItemWarnings(g_tx.mainqueue); logItemWarnings(g_tx.modules); logItemWarnings(g_tx.inputs); logItemWarnings(g_tx.templates); logItemWarnings(g_tx.rulesets); logItemWarnings(g_tx.lookups); logItemWarnings(g_tx.parsers); logItemWarnings(g_tx.timezones); logItemWarnings(g_tx.dynstats); logItemWarnings(g_tx.perctilestats); logItemWarnings(g_tx.ratelimits); } static void writeYamlQuoted(FILE *fp, const char *s) { fputc('"', fp); while (*s != '\0') { switch (*s) { case '"': fputs("\\\"", fp); break; case '\\': fputs("\\\\", fp); break; case '\n': fputs("\\n", fp); break; case '\r': fputs("\\r", fp); break; case '\t': fputs("\\t", fp); break; default: fputc(*s, fp); break; } ++s; } fputc('"', fp); } static void writeYamlValue(FILE *fp, const struct nvlst *n) { int i; char *s; switch (n->val.datatype) { case 'N': fprintf(fp, "%lld", n->val.d.n); break; case 'A': fputc('[', fp); for (i = 0; i < n->val.d.ar->nmemb; ++i) { s = es_str2cstr(n->val.d.ar->arr[i], NULL); if (i > 0) fputs(", ", fp); writeYamlQuoted(fp, s == NULL ? "" : s); free(s); } fputc(']', fp); break; case 'S': default: s = es_str2cstr(n->val.d.estr, NULL); writeYamlQuoted(fp, s == NULL ? "" : s); free(s); break; } } static int preferredKeyRank(const struct nvlst *n) { char *name; int rank = 3; if (n == NULL) return rank; name = es_str2cstr(n->name, NULL); if (name != NULL) { if (!strcmp(name, "name")) { rank = 0; } else if (!strcmp(name, "type")) { rank = 1; } else if (!strcmp(name, "load")) { rank = 2; } } free(name); return rank; } static int nvlstSortComesBefore(const struct nvlst *a, const struct nvlst *b) { char *nameA; char *nameB; int rankA; int rankB; int cmp; if (b == NULL) return 1; if (a == NULL) return 0; rankA = preferredKeyRank(a); rankB = preferredKeyRank(b); if (rankA != rankB) return rankA < rankB; nameA = es_str2cstr(a->name, NULL); nameB = es_str2cstr(b->name, NULL); if (nameA == NULL || nameB == NULL) { free(nameA); free(nameB); return 0; } cmp = strcmp(nameA, nameB); free(nameA); free(nameB); return cmp < 0; } static void writeYamlEntry(FILE *fp, const struct nvlst *n, int indent, int list_prefix) { char *name; int i; for (i = 0; i < indent; ++i) fputs(" ", fp); if (list_prefix) fputs("- ", fp); name = es_str2cstr(n->name, NULL); fprintf(fp, "%s: ", name == NULL ? "" : name); free(name); writeYamlValue(fp, n); fputc('\n', fp); } static void writeYamlMappingExcept(FILE *fp, const struct nvlst *lst, int indent, const struct nvlst *skip) { const struct nvlst *n; int rank; for (rank = 0; rank < 4; ++rank) { for (n = lst; n != NULL; n = n->next) { if (n == skip || preferredKeyRank(n) != rank) continue; writeYamlEntry(fp, n, indent, 0); } } } static void writeYamlMapping(FILE *fp, const struct nvlst *lst, int indent) { writeYamlMappingExcept(fp, lst, indent, NULL); } static void writeYamlBlockScalar(FILE *fp, const char *content, int indent) { const char *cur = content; int i; while (cur != NULL && *cur != '\0') { for (i = 0; i < indent; ++i) fputs(" ", fp); while (*cur != '\0' && *cur != '\n') { fputc(*cur++, fp); } fputc('\n', fp); if (*cur == '\n') ++cur; } } static void writeYamlActions(FILE *fp, const struct rsconfTranslateYamlAction_s *actions, int indent) { const struct rsconfTranslateYamlAction_s *act; int i; for (i = 0; i < indent; ++i) fputs(" ", fp); fputs("actions:\n", fp); for (act = actions; act != NULL; act = act->next) { const struct nvlst *n; const struct nvlst *firstNode = NULL; int firstRank = 4; for (n = act->nvlst; n != NULL; n = n->next) { int rank = preferredKeyRank(n); if (rank < firstRank) { firstNode = n; firstRank = rank; } } if (firstNode != NULL) { writeYamlEntry(fp, firstNode, indent + 1, 1); } else { for (i = 0; i < indent + 1; ++i) fputs(" ", fp); fputs("-\n", fp); } writeYamlMappingExcept(fp, act->nvlst, indent + 2, firstNode); } } static int yamlActionCount(const struct rsconfTranslateYamlAction_s *actions) { int n = 0; while (actions != NULL) { ++n; actions = actions->next; } return n; } static void writeYamlActionList(FILE *fp, const struct rsconfTranslateYamlAction_s *actions, int indent) { const struct rsconfTranslateYamlAction_s *act; int i; for (act = actions; act != NULL; act = act->next) { const struct nvlst *n; const struct nvlst *firstNode = NULL; int firstRank = 4; for (n = act->nvlst; n != NULL; n = n->next) { int rank = preferredKeyRank(n); if (rank < firstRank) { firstNode = n; firstRank = rank; } } if (firstNode != NULL) { writeYamlEntry(fp, firstNode, indent, 1); } else { for (i = 0; i < indent; ++i) fputs(" ", fp); fputs("-\n", fp); } writeYamlMappingExcept(fp, act->nvlst, indent + 1, firstNode); } } static void writeYamlActionMapping(FILE *fp, const struct nvlst *lst, int indent) { const struct nvlst *n; const struct nvlst *firstNode = NULL; int firstRank = 4; for (n = lst; n != NULL; n = n->next) { int rank = preferredKeyRank(n); if (rank < firstRank) { firstNode = n; firstRank = rank; } } if (firstNode != NULL) { writeYamlEntry(fp, firstNode, indent, 0); } writeYamlMappingExcept(fp, lst, indent, firstNode); } static void writeYamlStatements(FILE *fp, const struct rsconfTranslateYamlStatement_s *stmts, int indent) { const struct rsconfTranslateYamlStatement_s *stmt; int i; for (i = 0; i < indent; ++i) fputs(" ", fp); fputs("statements:\n", fp); for (stmt = stmts; stmt != NULL; stmt = stmt->next) { for (i = 0; i < indent + 1; ++i) fputs(" ", fp); fputs("- if: ", fp); writeYamlQuoted(fp, stmt->if_expr == NULL ? "" : stmt->if_expr); fputc('\n', fp); if (yamlActionCount(stmt->actions) == 1) { for (i = 0; i < indent + 2; ++i) fputs(" ", fp); fputs("action:\n", fp); writeYamlActionMapping(fp, stmt->actions->nvlst, indent + 3); } else { for (i = 0; i < indent + 2; ++i) fputs(" ", fp); fputs("then:\n", fp); writeYamlActionList(fp, stmt->actions, indent + 3); } } } static void writeYamlListSection(FILE *fp, const char *name, const struct rsconfTranslateItem_s *items) { const struct rsconfTranslateItem_s *it; if (items == NULL) return; fprintf(fp, "%s:\n", name); for (it = items; it != NULL; it = it->next) { const struct nvlst *n; const struct nvlst *firstNode = NULL; int firstRank = 4; writeWarningComments(fp, it->warnings, 1); for (n = it->nvlst; n != NULL; n = n->next) { int rank = preferredKeyRank(n); if (rank < firstRank) { firstNode = n; firstRank = rank; } } if (it->nvlst == NULL && it->script == NULL) { fputs(" -\n", fp); continue; } if (firstNode != NULL) { writeYamlEntry(fp, firstNode, 1, 1); } else { fputs(" -\n", fp); } writeYamlMappingExcept(fp, it->nvlst, 2, firstNode); if (it->subobjs != NULL) { fputs(" elements:\n", fp); for (const struct objlst *obj = it->subobjs; obj != NULL; obj = obj->next) { fprintf(fp, " - %s:\n", obj->obj->objType == CNFOBJ_PROPERTY ? "property" : "constant"); writeYamlMapping(fp, obj->obj->nvlst, 4); } } if (it->yaml_ruleset_kind == RS_TRANSLATE_YAML_RULESET_FILTER_ACTIONS && it->yaml_filter != NULL) { fputs(" filter: ", fp); writeYamlQuoted(fp, it->yaml_filter); fputc('\n', fp); writeYamlActions(fp, it->yaml_actions, 2); } else if (it->yaml_ruleset_kind == RS_TRANSLATE_YAML_RULESET_ACTIONS) { writeYamlActions(fp, it->yaml_actions, 2); } else if (it->yaml_ruleset_kind == RS_TRANSLATE_YAML_RULESET_STATEMENTS) { writeYamlStatements(fp, it->yaml_statements, 2); } else if (it->script != NULL) { fputs(" script: |\n", fp); writeYamlBlockScalar(fp, it->script, 3); } } } static void writeRsParams(FILE *fp, const struct nvlst *lst) { const struct nvlst *n; char *name; int first = 1, i, rank; for (rank = 0; rank < 4; ++rank) { for (n = lst; n != NULL; n = n->next) { if (preferredKeyRank(n) != rank) continue; if (!first) fputc(' ', fp); first = 0; name = es_str2cstr(n->name, NULL); fprintf(fp, "%s=", name == NULL ? "" : name); free(name); switch (n->val.datatype) { case 'N': fprintf(fp, "%lld", n->val.d.n); break; case 'A': fputc('[', fp); for (i = 0; i < n->val.d.ar->nmemb; ++i) { char *aval = es_str2cstr(n->val.d.ar->arr[i], NULL); if (i > 0) fputs(", ", fp); writeYamlQuoted(fp, aval == NULL ? "" : aval); free(aval); } fputc(']', fp); break; case 'S': default: { char *val = es_str2cstr(n->val.d.estr, NULL); writeYamlQuoted(fp, val == NULL ? "" : val); free(val); break; } } } } } static void writeRsTemplate(FILE *fp, const struct rsconfTranslateItem_s *it) { fprintf(fp, "template("); writeRsParams(fp, it->nvlst); if (it->subobjs == NULL) { fprintf(fp, ")\n\n"); return; } fprintf(fp, ") {\n"); for (const struct objlst *obj = it->subobjs; obj != NULL; obj = obj->next) { fprintf(fp, " %s(", obj->obj->objType == CNFOBJ_PROPERTY ? "property" : "constant"); writeRsParams(fp, obj->obj->nvlst); fprintf(fp, ")\n"); } fprintf(fp, "}\n\n"); } static void writeRsList(FILE *fp, const char *keyword, const struct rsconfTranslateItem_s *items) { const struct rsconfTranslateItem_s *it; for (it = items; it != NULL; it = it->next) { writeWarningComments(fp, it->warnings, 0); if (it->objType == CNFOBJ_TPL) { writeRsTemplate(fp, it); continue; } if (it->objType == CNFOBJ_RULESET) { fprintf(fp, "ruleset("); writeRsParams(fp, it->nvlst); if (it->script != NULL) { fprintf(fp, ") {\n%s}\n\n", it->script); } else { fprintf(fp, ") {}\n\n"); } continue; } fprintf(fp, "%s(", keyword); writeRsParams(fp, it->nvlst); fprintf(fp, ")\n\n"); } } rsRetVal rsconfTranslateWriteFile(const char *path) { FILE *fp = NULL; DEFiRet; if (!rsconfTranslateEnabled()) { FINALIZE; } if (g_tx.fatal) { logAllWarnings(); ABORT_FINALIZE(RS_RET_CONF_PARSE_ERROR); } if (path == NULL || !strcmp(path, "-")) { fp = stdout; } else { fp = fopen(path, "w"); } if (fp == NULL) { ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND); } if (g_tx.fmt == RSCONF_TRANSLATE_YAML) { fputs("version: 2\n\n", fp); writeWarningComments(fp, g_tx.globalWarnings, 0); if (g_tx.globals != NULL) { writeWarningComments(fp, g_tx.globals->warnings, 0); fputs("global:\n", fp); writeYamlMapping(fp, g_tx.globals->nvlst, 1); fputc('\n', fp); } if (g_tx.mainqueue != NULL) { writeWarningComments(fp, g_tx.mainqueue->warnings, 0); fputs("mainqueue:\n", fp); writeYamlMapping(fp, g_tx.mainqueue->nvlst, 1); fputc('\n', fp); } writeYamlListSection(fp, "modules", g_tx.modules); writeYamlListSection(fp, "inputs", g_tx.inputs); writeYamlListSection(fp, "templates", g_tx.templates); writeYamlListSection(fp, "rulesets", g_tx.rulesets); writeYamlListSection(fp, "lookup_tables", g_tx.lookups); writeYamlListSection(fp, "parsers", g_tx.parsers); writeYamlListSection(fp, "timezones", g_tx.timezones); writeYamlListSection(fp, "dyn_stats", g_tx.dynstats); writeYamlListSection(fp, "perctile_stats", g_tx.perctilestats); writeYamlListSection(fp, "ratelimits", g_tx.ratelimits); } else { writeWarningComments(fp, g_tx.globalWarnings, 0); writeRsList(fp, "global", g_tx.globals); writeRsList(fp, "main_queue", g_tx.mainqueue); writeRsList(fp, "module", g_tx.modules); writeRsList(fp, "input", g_tx.inputs); writeRsList(fp, "template", g_tx.templates); writeRsList(fp, "lookup_table", g_tx.lookups); writeRsList(fp, "parser", g_tx.parsers); writeRsList(fp, "timezone", g_tx.timezones); writeRsList(fp, "dyn_stats", g_tx.dynstats); writeRsList(fp, "perctile_stats", g_tx.perctilestats); writeRsList(fp, "ratelimit", g_tx.ratelimits); writeRsList(fp, "ruleset", g_tx.rulesets); } finalize_it: if (fp != NULL && fp != stdout) fclose(fp); RETiRet; }