fix(errors): перестать глотать сбои записи состояния в пустых catch
Пустых catch в кодовой базе около полусотни, и они не одинаковые. Большинство выражает намерение: fs.unlink бросает на ENOENT, а вызывающий говорит «пусть этого файла не будет», и отсутствующий файл это условие уже выполняет; разбор json в цикле пропускает мусорную строку и берёт следующую. Такие места теперь несут пояснение, почему пустое тело здесь и есть правильный ответ. Семь мест к этому классу не относились: там сбой означал потерю состояния, а дальше код шёл так, будто всё получилось. Watchdog писал через пустой catch файл лока перезапуска прокси, шкалу GOMEMLIMIT после OOM, бэкап конфига и метку keepalive. Не лёгший лок означает, что следующий тик перезапустит прокси заново, а счётчик rate-limit об этом не узнает; не записанная метка keepalive заставляет init.d считать watchdog мёртвым и перезапускать его. Все четыре теперь идут через write_state_file, который логирует отказ. Метка пишется на каждом тике uloop, поэтому безусловная строка залила бы syslog именно при переполненной файловой системе — там, где лог нужнее всего; вместо этого каждый путь сообщает о первом отказе и молчит до следующего успеха, что заодно делает видимым восстановление. Контроллер разбирал /etc/sing-box/config.json ради портов http и tproxy и при неудаче молча брал 4534 и 4530. Watchdog после этого опрашивает не тот порт, вечно считает прокси нерабочим и лечит его по кругу — при том, что в логе нет ничего, что связало бы это с конфигом. Разбор вынесен в общую функцию, которая сообщает о непарсящемся конфиге один раз на процесс. Telegram молча терял карту callback-токенов: токен уже вшит в клавиатуру к моменту записи, поэтому провал отдаёт пользователю кнопку, которая на нажатие ответит «кнопка устарела». Обновление списков молча теряло маркер отложенного применения nft — списки обновлены на диске и до nftables не доехали никогда, а строкой выше в лог написано, что маркер сохраняется. urltest молча выкидывал битое регулярное выражение из конфига пользователя, отчего группа тихо подхватывала меньше outbound-ов, чем сказано в конфиге. tests/heal_escalation.sh проверял порядок строк по литералу fs.writefile для лока и обновлён под новый вызов.
This commit is contained in:
@@ -194,6 +194,36 @@ function is_reload_in_progress() {
|
||||
return fs.stat("/var/run/tachyon.reload.lock") != null || check_tachyon_cli_running();
|
||||
}
|
||||
|
||||
// Reads a port out of the generated sing-box config. Both callers fall back to
|
||||
// a compiled-in default when the file cannot be parsed, and that fallback is
|
||||
// worth a log line: a watchdog probing 4534 while sing-box listens elsewhere
|
||||
// reports the proxy as down forever and heals it in a loop. The failure used to
|
||||
// be swallowed by an empty catch, so the symptom was a permanently unhealthy
|
||||
// proxy with nothing in the log to connect it to the config.
|
||||
//
|
||||
// Logged once per process — these are called on every tick, and a config that
|
||||
// fails to parse fails to parse every time.
|
||||
let sb_config_parse_reported = false;
|
||||
|
||||
function parse_singbox_config() {
|
||||
let data = fs.readfile("/etc/sing-box/config.json");
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
try {
|
||||
return json(data);
|
||||
}
|
||||
catch (e) {
|
||||
if (!sb_config_parse_reported) {
|
||||
sb_config_parse_reported = true;
|
||||
command_success_from_args([ "logger", "-t", "tachyon",
|
||||
"[err] Watchdog: /etc/sing-box/config.json is unparseable (" + as_string(e) +
|
||||
"); falling back to default inbound ports, proxy checks may probe the wrong port" ]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Reads the http/mixed inbound port from the generated sing-box config.
|
||||
// Cached because the config only changes across a reload.
|
||||
let cached_proxy_port = null;
|
||||
@@ -202,19 +232,14 @@ function proxy_port() {
|
||||
return as_string(cached_proxy_port);
|
||||
|
||||
let port = "4534";
|
||||
let sb_cfg_data = fs.readfile("/etc/sing-box/config.json");
|
||||
if (sb_cfg_data) {
|
||||
try {
|
||||
let sb_cfg = json(sb_cfg_data);
|
||||
if (sb_cfg.inbounds) {
|
||||
for (let inb in sb_cfg.inbounds) {
|
||||
if (inb.type == "http" || inb.type == "mixed") {
|
||||
port = as_string(inb.listen_port || 4534);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let sb_cfg = parse_singbox_config();
|
||||
if (sb_cfg && sb_cfg.inbounds) {
|
||||
for (let inb in sb_cfg.inbounds) {
|
||||
if (inb.type == "http" || inb.type == "mixed") {
|
||||
port = as_string(inb.listen_port || 4534);
|
||||
break;
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
cached_proxy_port = port;
|
||||
return port;
|
||||
@@ -222,23 +247,19 @@ function proxy_port() {
|
||||
|
||||
function forget_proxy_port() {
|
||||
cached_proxy_port = null;
|
||||
sb_config_parse_reported = false;
|
||||
}
|
||||
|
||||
function tproxy_port() {
|
||||
let port = 4530;
|
||||
let sb_cfg_data = fs.readfile("/etc/sing-box/config.json");
|
||||
if (sb_cfg_data) {
|
||||
try {
|
||||
let sb_cfg = json(sb_cfg_data);
|
||||
if (sb_cfg.inbounds) {
|
||||
for (let inb in sb_cfg.inbounds) {
|
||||
if (inb.type == "tproxy") {
|
||||
port = int(inb.listen_port || port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let sb_cfg = parse_singbox_config();
|
||||
if (sb_cfg && sb_cfg.inbounds) {
|
||||
for (let inb in sb_cfg.inbounds) {
|
||||
if (inb.type == "tproxy") {
|
||||
port = int(inb.listen_port || port);
|
||||
break;
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
return port;
|
||||
}
|
||||
@@ -816,6 +837,7 @@ function controller(bus, opts) {
|
||||
if (scale_data == null) return;
|
||||
let current_scale = double(trim(as_string(scale_data)));
|
||||
if (current_scale >= 1.0) {
|
||||
// Absent file already satisfies the caller; fs.unlink throws on ENOENT.
|
||||
try { fs.unlink(scale_path); } catch (e) {}
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user