Критические исправления: - safe_proxy_restart: удаление lock-файла после фонового перезапуска с защитой от stale lock (>300 сек) - write_json_file: атомарная запись через tmp+mv вместо прямой - write_reload_state: атомарная запись через tmp+mv - state_add_list: отклонение значений с пробелами Исправления безопасности: - killall logread заменён на pkill -f 'logread -f' - SIGTERM→SIGKILL для Telegram и sing-box при остановке - Recover OOM scale: инкремент +0.05 каждые 10 минут - nfqueue и byedpi: trap для корректного завершения дочерних процессов - nfqueue: валидация стратегий и откат при ошибке запуска - nfqueue: flush mangle_output перед добавлением правил - Исправлен парсинг exit code в 14 файлах (status & 127) Улучшения стабильности: - init.d: trap для start_service/stop_service - watchdog: signal traps для legacy fallback loop - lifecycle: логирование ошибок при остановке компонентов - singbox/priority: SIGTERM→SIGKILL при остановке - telegram: exponential backoff (1x→300s) при ошибках API - telegram: process_updates возвращает boolean для отслеживания успеха - watchdog: кэш proxy port для уменьшения чтения конфига sing-box - singbox/generator: упрощён atomic_write_json_file - packages.uc: переупорядочивание apk_version (лёгкие запросы первыми) - dns/apply: проверка PID sing-box перед настройкой dnsmasq - server/service: cleanup_orphaned_server_certs перед вызовом
191 lines
5.6 KiB
Ucode
191 lines
5.6 KiB
Ucode
#!/usr/bin/env ucode
|
|
|
|
let fs = require("fs");
|
|
let uci_core = require("core.uci");
|
|
|
|
function as_string(value) {
|
|
return value == null ? "" : "" + value;
|
|
}
|
|
|
|
function env(name, fallback) {
|
|
let value = getenv(name);
|
|
return value == null ? as_string(fallback) : as_string(value);
|
|
}
|
|
|
|
const CONFIG_NAME = env("TACHYON_CONFIG_NAME", "tachyon");
|
|
const RT_TABLES_PATH = env("TACHYON_RT_TABLES", "/etc/iproute2/rt_tables");
|
|
const BIN_PATH = env("TACHYON_BIN", "/usr/bin/tachyon");
|
|
const INIT_PATH = env("TACHYON_INIT", "/etc/init.d/tachyon");
|
|
const DNS_APPLY_UC = env("TACHYON_DNS_APPLY_UC", "/usr/lib/tachyon/dns/apply.uc");
|
|
const SING_BOX_INIT = env("TACHYON_SING_BOX_INIT", "/etc/init.d/sing-box");
|
|
const SING_BOX_BIN = env("TACHYON_SING_BOX_BIN", "/usr/bin/sing-box");
|
|
const SING_BOX_CRONET = env("TACHYON_SING_BOX_CRONET", "/usr/lib/libcronet.so");
|
|
const SING_BOX_MANAGED_MARKER = env("SB_MANAGED_SERVICE_MARKER", "Tachyon managed sing-box service for binary variants");
|
|
const PACKAGE_UPGRADE_STATE = env("TACHYON_PACKAGE_UPGRADE_STATE", "/tmp/tachyon-package-was-running");
|
|
const PACKAGE_TEST_MODE = env("TACHYON_PACKAGE_TEST_MODE", "") != "";
|
|
|
|
function shell_quote(value) {
|
|
return "'" + replace(as_string(value), /'/g, "'\\''") + "'";
|
|
}
|
|
|
|
function command_from_args(args) {
|
|
let parts = [];
|
|
for (let arg in args)
|
|
push(parts, shell_quote(arg));
|
|
return join(" ", parts);
|
|
}
|
|
|
|
function normalize_status(status) {
|
|
status = int(status);
|
|
if (status == -1)
|
|
return 255;
|
|
let signal = status & 127;
|
|
if (signal != 0)
|
|
return 128 + signal;
|
|
return (status >> 8) & 255;
|
|
}
|
|
|
|
function command_success_from_args(args) {
|
|
return normalize_status(system(command_from_args(args) + " >/dev/null 2>&1")) == 0;
|
|
}
|
|
|
|
function path_exists(path) {
|
|
return fs.stat(as_string(path)) != null;
|
|
}
|
|
|
|
function unlink_if_exists(path) {
|
|
if (path_exists(path))
|
|
fs.unlink(as_string(path));
|
|
}
|
|
|
|
function remove_rt_tables_entry() {
|
|
let data = fs.readfile(RT_TABLES_PATH);
|
|
if (data == null)
|
|
return true;
|
|
|
|
let changed = false;
|
|
let lines = [];
|
|
for (let line in split(data, "\n")) {
|
|
if (index(line, "105 tachyon") >= 0) {
|
|
changed = true;
|
|
continue;
|
|
}
|
|
push(lines, line);
|
|
}
|
|
|
|
return !changed || fs.writefile(RT_TABLES_PATH, join("\n", lines)) != null;
|
|
}
|
|
|
|
function ascii_lower(value) {
|
|
let upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
let lower = "abcdefghijklmnopqrstuvwxyz";
|
|
return replace(as_string(value), /[A-Z]/g, function(ch) {
|
|
return substr(lower, index(upper, ch), 1);
|
|
});
|
|
}
|
|
|
|
function truthy(value) {
|
|
value = ascii_lower(trim(as_string(value)));
|
|
return value == "1" || value == "true" || value == "yes" || value == "on";
|
|
}
|
|
|
|
function dont_touch_dhcp_enabled() {
|
|
return truthy(uci_core.get(CONFIG_NAME + ".settings.dont_touch_dhcp"));
|
|
}
|
|
|
|
function restore_dnsmasq_if_needed() {
|
|
if (dont_touch_dhcp_enabled())
|
|
return;
|
|
|
|
command_success_from_args([ BIN_PATH, "restore_dnsmasq" ]);
|
|
if (path_exists(DNS_APPLY_UC))
|
|
command_success_from_args([ "ucode", DNS_APPLY_UC, "failsafe-restore" ]);
|
|
}
|
|
|
|
function remove_managed_sing_box() {
|
|
let data = fs.readfile(SING_BOX_INIT);
|
|
if (data == null || index(data, SING_BOX_MANAGED_MARKER) < 0)
|
|
return;
|
|
|
|
command_success_from_args([ SING_BOX_INIT, "stop" ]);
|
|
command_success_from_args([ SING_BOX_INIT, "disable" ]);
|
|
unlink_if_exists(SING_BOX_INIT);
|
|
unlink_if_exists(SING_BOX_BIN);
|
|
unlink_if_exists(SING_BOX_CRONET);
|
|
}
|
|
|
|
function remember_upgrade_state(action) {
|
|
if (as_string(action) != "upgrade") {
|
|
unlink_if_exists(PACKAGE_UPGRADE_STATE);
|
|
return;
|
|
}
|
|
|
|
if (command_success_from_args([ INIT_PATH, "status" ]))
|
|
fs.writefile(PACKAGE_UPGRADE_STATE, "1\n");
|
|
}
|
|
|
|
function prerm_cleanup(action) {
|
|
if (env("IPKG_INSTROOT", "") != "")
|
|
return true;
|
|
|
|
remember_upgrade_state(action);
|
|
if (!PACKAGE_TEST_MODE) {
|
|
command_success_from_args([ INIT_PATH, "stop" ]);
|
|
restore_dnsmasq_if_needed();
|
|
remove_managed_sing_box();
|
|
}
|
|
return remove_rt_tables_entry();
|
|
}
|
|
|
|
function postinst_restore() {
|
|
if (env("IPKG_INSTROOT", "") != "" || !path_exists(PACKAGE_UPGRADE_STATE))
|
|
return true;
|
|
|
|
let started = command_success_from_args([ INIT_PATH, "start" ]);
|
|
unlink_if_exists(PACKAGE_UPGRADE_STATE);
|
|
return started;
|
|
}
|
|
|
|
function luci_cache_globs() {
|
|
let configured = env("TACHYON_LUCI_CACHE_GLOBS", "");
|
|
if (configured != "")
|
|
return split(configured, /[ \t\r\n]+/);
|
|
|
|
return [ "/var/luci-indexcache*", "/tmp/luci-indexcache*" ];
|
|
}
|
|
|
|
function remove_luci_index_cache() {
|
|
for (let pattern in luci_cache_globs()) {
|
|
pattern = as_string(pattern);
|
|
if (pattern == "")
|
|
continue;
|
|
|
|
for (let path in fs.glob(pattern))
|
|
unlink_if_exists(path);
|
|
}
|
|
}
|
|
|
|
function luci_postinst() {
|
|
remove_luci_index_cache();
|
|
if (!PACKAGE_TEST_MODE) {
|
|
if (path_exists("/etc/init.d/rpcd"))
|
|
command_success_from_args([ "/etc/init.d/rpcd", "reload" ]);
|
|
command_success_from_args([ "logger", "-t", "tachyon", "[info] Package defaults applied" ]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
let mode = ARGV[0] || "";
|
|
|
|
if (mode == "prerm")
|
|
exit(prerm_cleanup(ARGV[1]) ? 0 : 1);
|
|
else if (mode == "postinst")
|
|
exit(postinst_restore() ? 0 : 1);
|
|
else if (mode == "remove-rt-tables-entry")
|
|
exit(remove_rt_tables_entry() ? 0 : 1);
|
|
else if (mode == "luci-postinst")
|
|
exit(luci_postinst() ? 0 : 1);
|
|
else {
|
|
warn("Usage: service/package.uc <prerm|postinst|remove-rt-tables-entry|luci-postinst>\n");
|
|
exit(1);
|
|
} |