conf: drop the angle brackets from if, infer from the value

The block format spells conditions as bare strings everywhere else, so
requiring `if = "<usr/foo>"` left one sigil behind, carried over from
the line-based `if:` token.  A namespace separator already tells the two
apart: a value with a '/' is a condition, anything else is a service
name.

svc_ifthen() picks its mode from the start of the statement and applies
it to the whole, so a statement naming both kinds cannot be evaluated.
That is now an error, as are the old angle brackets, and either one
skips the block:

    /etc/finit.conf: mixed: if: cannot mix a service name with a
    condition in 'anchor,usr/enable-me', a statement must be all of
    one kind, skipping

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg committed 2026-07-30 15:23:36 +02:00
1 parent c584795202
commit be28436c2d
4 files changed
+182 -7

No files matched your search

+66 -2
View File
@@ -1230,6 +1230,65 @@ static void addlog(char *line, size_t len, cfg_t *sec, char *file)
* Translate service/task/run/sysv section to the canonical legacy
* one-liner and register through the same path as legacy files.
*/
/*
* if = "udevd" asks whether a service by that name is known, answered
* when the .conf is read, while if = "usr/foo" tests a condition,
* answered at runtime. The namespace separator tells the two apart,
* so the legacy angle brackets are not needed. A statement must be
* all of one kind; the two are not checked at the same time, so they
* cannot be combined.
*/
static int if_translate(const char *str, char *buf, size_t len, char *file, const char *ident)
{
char tmp[MAX_IDENT_LEN];
char *op, *save;
int cond = -1;
if (strpbrk(str, "<>")) {
logit(LOG_ERR, "%s: %s: if: angle brackets are not used in this"
" format, got '%s', skipping", file, ident, str);
return -1;
}
if (strlcpy(tmp, str, sizeof(tmp)) >= sizeof(tmp)) {
logit(LOG_ERR, "%s: %s: if: '%s' is too long, skipping",
file, ident, str);
return -1;
}
for (op = strtok_r(tmp, ",", &save); op; op = strtok_r(NULL, ",", &save)) {
int is_cond;
if (op[0] == '!')
op++;
if (!op[0])
continue;
is_cond = strchr(op, '/') != NULL;
if (cond == -1)
cond = is_cond;
else if (cond != is_cond) {
logit(LOG_ERR, "%s: %s: if: cannot mix a service name with a"
" condition in '%s', a statement must be all of one"
" kind, skipping", file, ident, str);
return -1;
}
}
if (cond == -1) {
logit(LOG_ERR, "%s: %s: if: no condition or service name in '%s',"
" skipping", file, ident, str);
return -1;
}
if (cond)
snprintf(buf, len, "<%s>", str);
else
strlcpy(buf, str, len);
return 0;
}
static void svc_translate(cfg_t *sec, int type, struct rlimit rlimit[], char *file)
{
struct rlimit local_rlimit[RLIMIT_NLIMITS];
@@ -1401,8 +1460,13 @@ static void svc_translate(cfg_t *sec, int type, struct rlimit rlimit[], char *fi
if (sec_getlist(sec, "conflicts", NULL, buf, sizeof(buf)))
addtok(line, sizeof(line), "conflict:%s", buf);
if ((str = sec_getstr(sec, "if", NULL)))
addopt(line, sizeof(line), "if:", str);
if ((str = sec_getstr(sec, "if", NULL))) {
char ifbuf[MAX_IDENT_LEN + 2];
if (if_translate(str, ifbuf, sizeof(ifbuf), file, cfg_title(sec)))
return;
addopt(line, sizeof(line), "if:", ifbuf);
}
if ((str = sec_getstr(sec, "tty", NULL)))
addtok(line, sizeof(line), "tty:%s", str);