Route every conf writer through the guarded path
Eleven call sites wrote master.conf with tmp+rename and nothing else — no backup, no parse check, no audit — including the two toggles the UI uses most and the raw editor that installs a whole hand-edited file.
This commit is contained in:
@@ -598,50 +598,56 @@ function vv_conf_flag_value(string $name): bool {
|
||||
}
|
||||
|
||||
// Write a boolean flag value to master.conf.
|
||||
// Goes through vv_conf_edit() for the lock, the pre-write backup, the syntax check and the audit
|
||||
// line. This used to call vv_write_conf_raw() directly, which gave it tmp+rename atomicity and
|
||||
// nothing else — no backup, and no check that the file still sourced afterwards.
|
||||
function vv_conf_flag_set(string $name, bool $value): bool {
|
||||
$confPath = CONF_DIR . '/master.conf';
|
||||
$content = file_get_contents($confPath);
|
||||
if ($content === false) return false;
|
||||
$val = $value ? 'true' : 'false';
|
||||
$new = preg_replace(
|
||||
'/^(\s*' . preg_quote($name, '/') . '\s*=\s*)(true|false)(\s*(?:#.*)?)$/m',
|
||||
'${1}' . $val . '${3}',
|
||||
$content, -1, $count
|
||||
);
|
||||
if (!$count) return false;
|
||||
// tmp+rename — every script sources master.conf, so a truncated write here is a
|
||||
// system-wide outage, not a lost toggle.
|
||||
return vv_write_conf_raw('master.conf', $new);
|
||||
if (!vv_conf_key_valid($name)) return false;
|
||||
$val = $value ? 'true' : 'false';
|
||||
|
||||
return vv_conf_edit('master.conf', function (string $content) use ($name, $val): ?string {
|
||||
$new = preg_replace(
|
||||
'/^(\s*' . preg_quote($name, '/') . '\s*=\s*)(true|false)(\s*(?:#.*)?)$/m',
|
||||
'${1}' . $val . '${3}',
|
||||
$content, -1, $count
|
||||
);
|
||||
// A name that matches no true/false line is a caller error, not an already-correct
|
||||
// state — unlike the membership toggle below, where absence genuinely means nothing
|
||||
// to do. Returning null keeps the write from happening and logs reason=no-match.
|
||||
return $count ? $new : null;
|
||||
}, [$name => $val]);
|
||||
}
|
||||
|
||||
// Comment or uncomment a script's line in the first master.conf array that contains it.
|
||||
// Goes through vv_conf_edit() for the lock, the pre-write backup, the syntax check and the audit
|
||||
// line — see vv_conf_flag_set() above for what that replaced. There is no key to verify here,
|
||||
// so the audit subject is the script id and a clean source is the whole assertion.
|
||||
function vv_conf_toggle_script(string $rel, bool $enable): bool {
|
||||
$confPath = CONF_DIR . '/master.conf';
|
||||
$lines = file($confPath, FILE_KEEP_BLANK_LINES);
|
||||
if (!$lines) return false;
|
||||
$changed = false;
|
||||
$inArray = false;
|
||||
$relEsc = preg_quote($rel, '/');
|
||||
foreach ($lines as &$line) {
|
||||
if (preg_match('/^\s*[A-Z_]+_SCRIPTS\s*=\s*\(/', $line)) $inArray = true;
|
||||
if ($inArray && preg_match('/^\s*\)\s*(?:#.*)?$/', $line) && !str_contains($line, '(')) $inArray = false;
|
||||
if (!$inArray) continue;
|
||||
if (!preg_match('/^\s*(?:#\s*)?"' . $relEsc . '(?:\s[^"]*)?"/', $line)) continue;
|
||||
$isCommented = (bool)preg_match('/^\s*#/', $line);
|
||||
if ($enable && $isCommented) {
|
||||
$line = preg_replace('/^(\s*)#\s*("' . $relEsc . ')/', '$1$2', $line);
|
||||
$changed = true;
|
||||
} elseif (!$enable && !$isCommented) {
|
||||
$line = preg_replace('/^(\s*)("' . $relEsc . ')/', '$1# $2', $line);
|
||||
$changed = true;
|
||||
return vv_conf_edit('master.conf', function (string $content) use ($rel, $enable): ?string {
|
||||
$lines = preg_split('/(?<=\n)/', $content) ?: [];
|
||||
$changed = false;
|
||||
$inArray = false;
|
||||
$relEsc = preg_quote($rel, '/');
|
||||
foreach ($lines as &$line) {
|
||||
if (preg_match('/^\s*[A-Z_]+_SCRIPTS\s*=\s*\(/', $line)) $inArray = true;
|
||||
if ($inArray && preg_match('/^\s*\)\s*(?:#.*)?$/', $line) && !str_contains($line, '(')) $inArray = false;
|
||||
if (!$inArray) continue;
|
||||
if (!preg_match('/^\s*(?:#\s*)?"' . $relEsc . '(?:\s[^"]*)?"/', $line)) continue;
|
||||
$isCommented = (bool)preg_match('/^\s*#/', $line);
|
||||
if ($enable && $isCommented) {
|
||||
$line = preg_replace('/^(\s*)#\s*("' . $relEsc . ')/', '$1$2', $line);
|
||||
$changed = true;
|
||||
} elseif (!$enable && !$isCommented) {
|
||||
$line = preg_replace('/^(\s*)("' . $relEsc . ')/', '$1# $2', $line);
|
||||
$changed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
unset($line);
|
||||
if (!$changed) return true;
|
||||
// tmp+rename — every script sources master.conf, so a truncated write here is a
|
||||
// system-wide outage, not a lost toggle.
|
||||
return vv_write_conf_raw('master.conf', implode('', $lines));
|
||||
unset($line);
|
||||
// A script in no array has nothing to toggle and the conf already reads the way the
|
||||
// caller asked. Returning the content unchanged reports success without a write.
|
||||
return $changed ? implode('', $lines) : $content;
|
||||
}, [], [$rel]);
|
||||
}
|
||||
|
||||
// Parse an orchestrator script to find which child scripts it calls.
|
||||
|
||||
Reference in New Issue
Block a user