Document the PHP api layer and fix what documenting it exposed
Writing down what each endpoint actually guarantees made the places it didn't obvious — shell arguments reaching a crontab or a bash -c unescaped, master.conf written without tmp+rename, and conf edits that could be saved without ever being parsed.
This commit is contained in:
@@ -282,7 +282,23 @@ function vv_conf_write_changes(array $changes): array {
|
||||
) ?? $raw;
|
||||
}
|
||||
}
|
||||
$results[$file] = vv_write_conf_raw($file, $raw);
|
||||
// Only the scalar path escapes its value; the array, array_single and assoc_array
|
||||
// paths splice the caller's text into the file verbatim, and the type comes from the
|
||||
// request. Every script sources these files, so the result is parsed before it is
|
||||
// allowed to replace a working conf.
|
||||
$results[$file] = vv_conf_syntax_ok($raw) && vv_write_conf_raw($file, $raw);
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
// bash -n against a private temp copy. Returns true when the content parses as a sourceable
|
||||
// conf, false otherwise — never writes anything itself.
|
||||
function vv_conf_syntax_ok(string $content): bool {
|
||||
$tmp = tempnam(sys_get_temp_dir(), 'vvconf');
|
||||
if ($tmp === false) return true; // cannot check — do not block the write
|
||||
file_put_contents($tmp, $content);
|
||||
$out = []; $rc = 0;
|
||||
exec('bash -n ' . escapeshellarg($tmp) . ' 2>&1', $out, $rc);
|
||||
@unlink($tmp);
|
||||
return $rc === 0;
|
||||
}
|
||||
|
||||
@@ -476,7 +476,12 @@ function vv_auto_create_api_key(string $hostId, string $confFile): array {
|
||||
if (!file_exists($script)) {
|
||||
return ['ok' => false, 'error' => 'unraid_api_key_renew.sh not found'];
|
||||
}
|
||||
exec('bash ' . escapeshellarg($script) . ' 2>&1', $out, $rc);
|
||||
// set_time_limit() does not cover exec() time on Linux, so the bound has to be external —
|
||||
// otherwise a stalled unraid-api call holds a php-fpm worker open indefinitely.
|
||||
exec('timeout 120 bash ' . escapeshellarg($script) . ' 2>&1', $out, $rc);
|
||||
if ($rc === 124) {
|
||||
return ['ok' => false, 'error' => 'Key renewal timed out after 120s'];
|
||||
}
|
||||
if ($rc !== 0) {
|
||||
$msg = implode(' ', array_filter(array_map('trim', $out)));
|
||||
return ['ok' => false, 'error' => $msg ?: 'Script failed'];
|
||||
|
||||
@@ -579,7 +579,9 @@ function vv_conf_flag_set(string $name, bool $value): bool {
|
||||
$content, -1, $count
|
||||
);
|
||||
if (!$count) return false;
|
||||
return file_put_contents($confPath, $new) !== 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);
|
||||
}
|
||||
|
||||
// Comment or uncomment a script's line in the first master.conf array that contains it.
|
||||
@@ -607,7 +609,9 @@ function vv_conf_toggle_script(string $rel, bool $enable): bool {
|
||||
}
|
||||
unset($line);
|
||||
if (!$changed) return true;
|
||||
return file_put_contents($confPath, implode('', $lines)) !== 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', implode('', $lines));
|
||||
}
|
||||
|
||||
// Parse an orchestrator script to find which child scripts it calls.
|
||||
|
||||
Reference in New Issue
Block a user