Files
NeoTachyon/tachyon/files/usr/lib/core/common.uc
T
Dushnilin 488bbfefd5 fix: комплексный аудит и исправление ucode кодовой базы
Критические исправления:
- 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 перед вызовом
2026-08-06 00:03:51 +03:00

229 lines
5.2 KiB
Ucode

#!/usr/bin/env ucode
let fs = require("fs");
global.double = function(v) {
if (v == null || v == "")
return 0.0;
return v * 1.0;
};
function as_string(value) {
return value == null ? "" : "" + value;
}
function read_json_file(path) {
let data = fs.readfile(path);
if (data == null)
return null;
try {
return json(data);
}
catch (e) {
return null;
}
}
function read_stdin() {
let input = fs.open("/dev/stdin", "r");
if (!input)
return "";
let data = input.read("all");
input.close();
return data == null ? "" : data;
}
function read_stdin_json() {
let data = read_stdin();
try {
return json(data);
}
catch (e) {
return null;
}
}
function write_json(value) {
print(sprintf("%J", value), "\n");
}
function write_compact_string_array(values) {
print("[");
for (let i = 0; i < length(values); i++) {
if (i > 0)
print(",");
print(sprintf("%J", as_string(values[i])));
}
print("]\n");
}
function csv_to_json_array(value) {
value = as_string(value);
write_compact_string_array(value == "" ? [] : split(value, ","));
}
function write_json_file(path, value) {
path = as_string(path);
let stamp = clock();
let tmp_path = sprintf("%s.%d.%d.tmp", path, stamp[0], stamp[1]);
let result = fs.writefile(tmp_path, sprintf("%J\n", value));
if (result == null || (type(result) == "boolean" && !result)) {
try { fs.unlink(tmp_path); } catch(e) {}
return false;
}
if (!fs.rename(tmp_path, path)) {
try { fs.unlink(tmp_path); } catch(e) {}
return false;
}
return true;
}
function strip_internal_fields(value) {
if (type(value) == "array") {
for (let i = 0; i < length(value); i++)
value[i] = strip_internal_fields(value[i]);
return value;
}
if (type(value) == "object") {
for (let key in keys(value)) {
if (substr(key, 0, 2) == "__") {
delete value[key];
continue;
}
value[key] = strip_internal_fields(value[key]);
}
}
return value;
}
function array_or_empty(value) {
return type(value) == "array" ? value : [];
}
function object_or_empty(value) {
return type(value) == "object" ? value : {};
}
function object_key_count(value) {
return type(value) == "object" ? length(keys(value)) : 0;
}
function option(section, key, fallback) {
if (fallback == null)
fallback = "";
let value = object_or_empty(section)[key];
if (value == null)
return fallback;
if (type(value) == "array")
return join(" ", value);
return as_string(value);
}
function list_option(section, key) {
let value = object_or_empty(section)[key];
if (value == null)
return [];
if (type(value) == "array")
return value;
let text = trim(as_string(value));
return text == "" ? [] : split(text, /[ \t\r\n]+/);
}
function bool_option(section, key, fallback) {
if (fallback == null)
fallback = false;
let value = option(section, key, fallback ? "1" : "0");
return value == "1" || value == "true" || value == "yes" || value == "on";
}
function int_option(section, key, fallback) {
let value = option(section, key, fallback);
if (match(value, /[^0-9]/))
return int(fallback, 10);
return int(value, 10);
}
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 command_status(command) {
let status = int(system(command));
if (status == -1)
return 255;
let signal = status & 127;
if (signal != 0)
return 128 + signal;
return (status >> 8) & 255;
}
function command_success(command) {
return command_status("(" + command + ") >/dev/null 2>&1") == 0;
}
function command_success_from_args(args) {
return command_success(command_from_args(args));
}
function command_capture(command) {
let p = fs.popen(command, "r");
if (!p) return null;
let output = as_string(p.read("all") || "");
let status = p.close();
if (status == -1)
status = 255;
else {
let signal = status & 127;
if (signal != 0)
status = 128 + signal;
else
status = (status >> 8) & 255;
}
return { status: status, output: output };
}
function command_output(command) {
let res = command_capture(command);
return res ? res.output : "";
}
function command_output_from_args(args) {
return command_output(command_from_args(args) + " 2>/dev/null");
}
return {
as_string,
read_json_file,
read_stdin,
read_stdin_json,
write_json,
write_compact_string_array,
csv_to_json_array,
write_json_file,
strip_internal_fields,
array_or_empty,
object_or_empty,
object_key_count,
option,
list_option,
bool_option,
int_option,
shell_quote,
command_from_args,
command_status,
command_success,
command_success_from_args,
command_capture,
command_output,
command_output_from_args
};