runtime: map common legacy selector actions to YAML

Why:
Common distro defaults still use traditional selector/action lines.
Translating those into native YAML makes migration output easier to
review and edit than falling back to script text.

Impact: Common legacy file and omusrmsg actions now emit structured
YAML, with docs and tests covering the supported subset.

Before/After:
Before, lines like user.* -/var/log/user.log fell back to warnings and
script output. After, they become structured YAML filter/actions or
YAML-native statements for repeated top-level selector lines.

Technical Overview:
Teach the translation layer to recognize a limited set of legacy action
forms and convert them into normal action parameter lists.
Support /path and -/path as omfile actions and :omusrmsg:... as
omusrmsg actions.
Promote repeated top-level selector lines into YAML-native statements
under RSYSLOG_DefaultRuleset instead of overwriting earlier entries.
Prefer prifilt('...') for readable constant selector expressions, with a
fallback to double quotes if the selector contains a single quote.
Add focused shell tests for single-line shorthand and a Debian-style
legacy default snippet.
Document the new legacy translation support and clearly state that it is
limited and does not cover all legacy constructs.

With the help of AI-Agents: Codex
This commit is contained in:
Rainer Gerhards 2026-04-03 16:26:38 +02:00
parent 696ad80ff9
commit 6b7e0686c0
7 changed files with 602 additions and 40 deletions

View File

@ -16,6 +16,12 @@ directly between canonical RainerScript and YAML. See
:doc:`../tutorials/config_format_translation` for the built-in translation
workflow.
The built-in translator currently recognizes a limited subset of traditional
selector/action syntax for structured YAML output. Common file actions
(``/var/log/...`` and ``-/var/log/...``) and ``:omusrmsg:...`` are covered,
but broader legacy syntax still requires review and may fall back to warnings
or raw script output.
Do not overdo conversion
~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -47,7 +47,9 @@ runtime semantics.
If you want to migrate an existing RainerScript config into YAML, rsyslog can
generate canonical YAML directly via ``rsyslogd -N1 -F yaml -o ...``. Simple
rulesets are emitted as structured YAML such as ``actions:`` or ``filter:`` +
``actions:``, while more complex bodies fall back to ``script: |``. See
``actions:``, and a limited set of common legacy selector/action forms is
normalized into structured YAML ``statements:``. More complex bodies still
fall back to ``script: |``. See
:doc:`../tutorials/config_format_translation` for the workflow and caveats.
When to Use YAML or RainerScript

View File

@ -30,6 +30,8 @@ The translator is a semantic migration tool:
- It flattens include trees into one generated file.
- It may emit warning comments for constructs that are easy to misunderstand,
especially legacy syntax.
- It can normalize a limited set of common legacy selector/action lines into
structured YAML instead of falling back to raw RainerScript text.
It is not a source-preserving formatter:
@ -37,6 +39,10 @@ It is not a source-preserving formatter:
- Output is normalized into one canonical file.
- The generated YAML or RainerScript may look different from the original even
when the behavior is unchanged.
- Legacy coverage is intentionally limited. Common file actions
(``/var/log/...`` and ``-/var/log/...``) and ``:omusrmsg:...`` are
recognized, but many other legacy forms still fall back to warnings or
``script: |`` output.
Translate RainerScript to YAML
------------------------------
@ -112,6 +118,47 @@ For simple rulesets, translation emits structured YAML such as ``actions:`` or
``filter:`` + ``actions:``. More complex logic still falls back to a YAML
``script: |`` block so semantics remain explicit.
Limited Legacy Translation
--------------------------
Common distro defaults written in traditional selector/action syntax can now be
translated into native YAML structures.
For example, this legacy input:
.. code-block:: rsyslog
user.* -/var/log/user.log
*.emerg :omusrmsg:*
is translated into YAML like:
.. code-block:: yaml
version: 2
rulesets:
- name: "RSYSLOG_DefaultRuleset"
statements:
- if: "prifilt('user.*')"
action:
type: "omfile"
file: "/var/log/user.log"
- if: "prifilt('*.emerg')"
action:
type: "omusrmsg"
users: "*"
Current legacy coverage is intentionally narrow:
- file actions written as ``/path`` or ``-/path`` become ``omfile`` actions
- ``:omusrmsg:user`` becomes ``omusrmsg`` with ``users: ...``
- selector chains are normalized into YAML ``statements:``
Do not expect all legacy constructs to be converted into structured YAML.
Unsupported or ambiguous cases may still produce warning comments or fall back
to ``script: |``.
Translate YAML to RainerScript
------------------------------

View File

@ -38,6 +38,10 @@ 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;
@ -51,10 +55,17 @@ struct rsconfTranslateYamlAction_s {
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 {
@ -68,6 +79,7 @@ struct rsconfTranslateItem_s {
enum rsconfTranslateYamlRulesetKind_e yaml_ruleset_kind;
char *yaml_filter;
struct rsconfTranslateYamlAction_s *yaml_actions;
struct rsconfTranslateYamlStatement_s *yaml_statements;
struct rsconfTranslateItem_s *next;
};
@ -123,6 +135,16 @@ static void yamlActionDestruct(struct rsconfTranslateYamlAction_s *act) {
}
}
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;
@ -133,6 +155,7 @@ static void itemDestruct(struct rsconfTranslateItem_s *it) {
warningsDestruct(it->warnings);
free(it->yaml_filter);
yamlActionDestruct(it->yaml_actions);
yamlStatementDestruct(it->yaml_statements);
free(it);
it = n;
}
@ -354,25 +377,183 @@ static struct objlst *cloneObjlst(const struct objlst *lst) {
static int stmtListIsStructuredActionsOnly(const struct cnfstmt *stmt) {
while (stmt != NULL) {
if (stmt->nodetype != S_ACT || stmt->d.act == NULL || stmt->d.act->pSyntaxLst == NULL) return 0;
stmt = stmt->next;
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) goto done;
node = nvlstSetName(nvlstNewStr(val), 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;
}
node->nvlst = rsconfTranslateCloneNvlst(stmt->d.act->pSyntaxLst);
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 = 1;
*oom = legacy_oom;
free(node);
yamlActionDestruct(head);
return NULL;
@ -400,6 +581,142 @@ static void yamlActionsAppend(struct rsconfTranslateYamlAction_s **dst, struct r
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 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;
}
stmt = newYamlStatement(if_expr, it->yaml_actions, oom);
if (stmt == NULL) return 0;
it->yaml_actions = NULL;
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;
@ -412,20 +729,27 @@ static void captureYamlRulesetBody(struct rsconfTranslateItem_s *it, const struc
g_tx.fatal = 1;
return;
}
if (it->yaml_ruleset_kind == RS_TRANSLATE_YAML_RULESET_NONE) {
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;
}
return;
}
if (script->next == NULL && script->printable != NULL) {
@ -451,7 +775,26 @@ static void captureYamlRulesetBody(struct rsconfTranslateItem_s *it, const struc
g_tx.fatal = 1;
return;
}
it->yaml_ruleset_kind = RS_TRANSLATE_YAML_RULESET_FILTER_ACTIONS;
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;
}
}
@ -460,6 +803,8 @@ static void captureYamlRulesetBody(struct rsconfTranslateItem_s *it, const struc
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;
}
@ -692,36 +1037,25 @@ static int nvlstValueToRs(es_str_t **out, const struct nvlst *node) {
static int emitActionSingleline(es_str_t **out,
const struct cnfstmt *stmt,
struct rsconfTranslateWarning_s **warnings) {
const struct nvlst *lst;
char *name;
int first = 1;
switch (stmt->nodetype) {
case S_ACT:
if (stmt->d.act != NULL && stmt->d.act->pSyntaxLst != NULL) {
int rank;
if (estrAppendCstr(out, "action(") != 0) return -1;
for (rank = 0; rank < 4; ++rank) {
for (lst = stmt->d.act->pSyntaxLst; lst != NULL; lst = lst->next) {
if (preferredKeyRank(lst) != rank) continue;
name = es_str2cstr(lst->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, lst) != 0) {
free(name);
return -1;
}
free(name);
}
}
return estrAppendChar(out, ')');
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);
}
@ -729,13 +1063,15 @@ static int emitActionSingleline(es_str_t **out,
return -1;
case S_STOP:
return estrAppendCstr(out, "stop");
case S_CALL:
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;
first = estrAppendCstr(out, name);
ret = estrAppendCstr(out, name);
free(name);
return first;
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;
@ -1026,6 +1362,7 @@ void rsconfTranslateCaptureScript(const struct cnfstmt *script, const char *sour
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) {
@ -1067,6 +1404,7 @@ void rsconfTranslateCaptureScript(const struct cnfstmt *script, const char *sour
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");
@ -1101,7 +1439,25 @@ void rsconfTranslateCaptureScript(const struct cnfstmt *script, const char *sour
free(more);
}
es_deleteStr(estr);
captureYamlRulesetBody(it, script);
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) {
@ -1310,6 +1666,81 @@ static void writeYamlActions(FILE *fp, const struct rsconfTranslateYamlAction_s
}
}
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;
@ -1350,6 +1781,8 @@ static void writeYamlListSection(FILE *fp, const char *name, const struct rsconf
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);

View File

@ -559,6 +559,7 @@ TESTS_LIBGCRYPT_VALGRIND = \
TESTS_LIBYAML = \
config-translate-rs-to-yaml.sh \
config-translate-rs-filter-actions.sh \
config-translate-legacy-debian-default.sh \
config-translate-legacy-file-action.sh \
config-translate-yaml-to-rs.sh \
ratelimit_hup.sh \

View File

@ -0,0 +1,64 @@
#!/bin/bash
# check translation of Debian-style legacy default rules into YAML statements.
#
# Part of the testbench for rsyslog.
#
# This file is part of rsyslog.
# Released under ASL 2.0
. ${srcdir:=.}/diag.sh init
export ASAN_OPTIONS="${ASAN_OPTIONS:-detect_leaks=0}"
modpath="../runtime/.libs:../.libs"
cat > "${RSYSLOG_DYNNAME}.conf" <<'RS_EOF'
*.*;auth,authpriv.none -/var/log/syslog
auth,authpriv.* /var/log/auth.log
cron.* -/var/log/cron.log
kern.* -/var/log/kern.log
mail.* -/var/log/mail.log
user.* -/var/log/user.log
*.emerg :omusrmsg:*
RS_EOF
../tools/rsyslogd -N1 -f "${RSYSLOG_DYNNAME}.conf" -F yaml -o "${RSYSLOG_DYNNAME}.yaml" -M"$modpath" || error_exit $?
cat -n "${RSYSLOG_DYNNAME}.yaml"
cat > "${RSYSLOG_DYNNAME}.expected.yaml" <<'YAML_EOF'
version: 2
rulesets:
# TRANSLATION WARNING: top-level statements normalized into explicit RSYSLOG_DefaultRuleset
- name: "RSYSLOG_DefaultRuleset"
statements:
- if: "prifilt('*.*;auth,authpriv.none')"
action:
type: "omfile"
file: "/var/log/syslog"
- if: "prifilt('auth,authpriv.*')"
action:
type: "omfile"
file: "/var/log/auth.log"
- if: "prifilt('cron.*')"
action:
type: "omfile"
file: "/var/log/cron.log"
- if: "prifilt('kern.*')"
action:
type: "omfile"
file: "/var/log/kern.log"
- if: "prifilt('mail.*')"
action:
type: "omfile"
file: "/var/log/mail.log"
- if: "prifilt('user.*')"
action:
type: "omfile"
file: "/var/log/user.log"
- if: "prifilt('*.emerg')"
action:
type: "omusrmsg"
users: "*"
YAML_EOF
cmp_exact_file "${RSYSLOG_DYNNAME}.expected.yaml" "${RSYSLOG_DYNNAME}.yaml"
echo SUCCESS: Debian-style legacy defaults translate into YAML statements
exit_test

View File

@ -1,5 +1,5 @@
#!/bin/bash
# check translation warnings for traditional file action shorthand.
# check translation of traditional file action shorthand into YAML filter/actions.
#
# Many distros still ship defaults like:
# user.* -/var/log/user.log
@ -18,11 +18,20 @@ user.* -/var/log/user.log
RS_EOF
../tools/rsyslogd -N1 -f "${RSYSLOG_DYNNAME}.conf" -F yaml -o "${RSYSLOG_DYNNAME}.yaml" -M"$modpath" || error_exit $?
cat -n "${RSYSLOG_DYNNAME}.yaml"
content_check '# TRANSLATION WARNING: top-level statements normalized into explicit RSYSLOG_DefaultRuleset' "${RSYSLOG_DYNNAME}.yaml"
content_check '# TRANSLATION WARNING: legacy action syntax preserved as script text' "${RSYSLOG_DYNNAME}.yaml"
content_check 'name: "RSYSLOG_DefaultRuleset"' "${RSYSLOG_DYNNAME}.yaml"
content_check 'user.* -/var/log/user.log' "${RSYSLOG_DYNNAME}.yaml"
cat > "${RSYSLOG_DYNNAME}.expected.yaml" <<'YAML_EOF'
version: 2
rulesets:
# TRANSLATION WARNING: top-level statements normalized into explicit RSYSLOG_DefaultRuleset
- name: "RSYSLOG_DefaultRuleset"
filter: "user.*"
actions:
- type: "omfile"
file: "/var/log/user.log"
YAML_EOF
cmp_exact_file "${RSYSLOG_DYNNAME}.expected.yaml" "${RSYSLOG_DYNNAME}.yaml"
echo SUCCESS: legacy file-action shorthand translation coverage
exit_test