Protect lifecycle producers and leave fatal signals to the platform

This commit is contained in:
evgeny committed 2026-10-29 13:35:41 +01:00
1 parent 929df3b6cb
commit 5a4ddca190
3 files changed
+37 -99

No files matched your search

+4
View File
@@ -315,6 +315,10 @@ static int check_worker_requests(const char* directory) {
REQUIRE(instance_lite_post(worker_payload_run, &payload, NULL, worker_payload_discard) == -1 && payload == -1);
REQUIRE(!utun_bridge_clear_database(path));
char keys[1100]; snprintf(keys, sizeof(keys), "%s/keys.conf", path); REQUIRE(!test_unlink(keys));
const char* folders[] = {"member_files/.uploads", "member_files", "dm_pending", "dm_media"};
for (unsigned i = 0; i < sizeof(folders) / sizeof(folders[0]); i++) {
snprintf(keys, sizeof(keys), "%s/%s", path, folders[i]); REQUIRE(!test_rmdir(keys));
}
REQUIRE(!test_rmdir(path));
printf("[PASS] worker requests: real worker read, nested call, stopped queue discard and waiter cancellation\n");
return 0;
@@ -7,7 +7,6 @@
*
* UTUN выключен по умолчанию; [gui] utun_enabled=yes явно включает сервис при старте и рестарте.
*/
#define _GNU_SOURCE 1 /* dladdr (Linux); no-op on Android/bionic */
#include "instance_lite.h"
#include "utun_instance.h"
#include "config_parser.h"
@@ -42,9 +41,6 @@
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <unwind.h>
#include <dlfcn.h>
#include <time.h>
#include <errno.h>
@@ -348,55 +344,6 @@ static void on_link_status_changed(struct ETCP_CONN* conn, struct ETCP_LINK* lin
fire_local_sockets_event();
}
/* ── Crash handler ── */
static volatile int g_signal_caught = 0;
static volatile int g_signal_num = 0;
struct crash_bt_state { void** frames; int count; int max; };
static _Unwind_Reason_Code crash_bt_cb(struct _Unwind_Context* ctx, void* arg) {
struct crash_bt_state* st = (struct crash_bt_state*)arg;
if (st->count < st->max) {
void* pc = (void*)_Unwind_GetIP(ctx);
if (pc) st->frames[st->count++] = pc;
return _URC_NO_REASON;
}
return _URC_END_OF_STACK;
}
static void crash_signal_handler(int sig, siginfo_t* info, void* ctx) {
(void)ctx;
g_signal_caught = 1; g_signal_num = sig;
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "CRASH signal=%d code=%d addr=%p thread still alive", sig, info->si_code, info->si_addr);
void* frames[64];
struct crash_bt_state st = { frames, 0, 64 };
_Unwind_Backtrace(crash_bt_cb, &st);
IL_LOGE("CRASH backtrace %d frames:", st.count);
for (int i = 0; i < st.count; i++) {
Dl_info dli;
if (dladdr(frames[i], &dli) && dli.dli_sname) {
IL_LOGE(" #%02d %p %s+%p (%s)", i, frames[i], dli.dli_sname,
(void*)((char*)frames[i] - (char*)dli.dli_saddr),
dli.dli_fname ? dli.dli_fname : "?");
} else {
IL_LOGE(" #%02d %p (no symbol)", i, frames[i]);
}
}
signal(sig, SIG_DFL);
raise(sig);
}
static void install_crash_handlers(void) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = crash_signal_handler;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGABRT, &sa, NULL);
sigaction(SIGBUS, &sa, NULL);
}
/* ── Thread function ── */
static void log_active_sockets(struct UTUN_INSTANCE* inst, const char* label);
@@ -434,7 +381,6 @@ static void* instance_thread(void* arg) {
if (!config) { IL_LOGE("parse_config_from_buf failed"); goto cleanup; }
config->global.client_type = CLIENT_TYPE_MOBILE; /* Android — всегда мобильный узел (keepalive standby/normal) */
install_crash_handlers();
if (ensure_keys(config)) {
IL_LOGE("startup refused: Android identity is not saved");
free_config(config);
@@ -799,8 +745,8 @@ static int request_restart(const char* new_config_text, int regenerate_keys) {
return 0;
}
void instance_lite_restart(const char* new_config_text) {
request_restart(new_config_text, 0);
int instance_lite_restart(const char* new_config_text) {
return request_restart(new_config_text, 0);
}
/* Принимает владение ненулевым task при любом результате. Все данные команды находятся в том же выделении
@@ -932,74 +878,62 @@ static void update_sockets_trampoline(void* arg) {
u_free(cfg_text);
return;
}
log_active_sockets(inst, "update OLD sockets");
struct utun_config* cfg = parse_config_from_buf(cfg_text, strlen(cfg_text), "android-update");
u_free(cfg_text);
if (!cfg) { IL_LOGE("update_sockets: parse_config_from_buf failed"); return; }
auto_socket_reload_config(inst, cfg); /* cfg владеет auto_socket: servers переносятся, остальное освобождается */
}
void instance_lite_update_sockets(const char* config_text) {
if (!config_text) return;
struct UASYNC* ua = (struct UASYNC*)__atomic_load_n(&g_ua, __ATOMIC_ACQUIRE);
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)__atomic_load_n(&g_inst, __ATOMIC_ACQUIRE);
int running = __atomic_load_n(&g_running, __ATOMIC_ACQUIRE);
/* Диагностика смены сети: какие сокеты/адреса были → какие будут (без полного рестарта). */
log_active_sockets(inst, "update OLD sockets");
int instance_lite_update_sockets(const char* config_text) {
if (!config_text) { IL_LOGE("update_sockets: config is NULL"); return -1; }
log_config_servers(config_text, "update NEW config");
if (!ua || !inst || !running) {
IL_LOGE("update_sockets: instance not running (ua=%p inst=%p running=%d) — update dropped (no fallback restart)",
(void*)ua, (void*)inst, running);
return;
}
char* copy = u_strdup(config_text);
if (!copy) { IL_LOGE("update_sockets: u_strdup failed"); return; }
uasync_post(ua, update_sockets_trampoline, copy);
if (!copy) { IL_LOGE("update_sockets: config allocation failed"); return -1; }
if (instance_lite_post(update_sockets_trampoline, copy, NULL, NULL)) {
IL_LOGE("update_sockets: core not ready, update rejected"); u_free(copy); return -1;
}
return 0;
}
/* ── Health check ping ── */
static volatile int g_ping_id = 0;
static volatile int g_pong_id = 0;
static uint64_t g_ping_id;
static uint64_t g_pong_id;
static void ping_trampoline(void* arg) {
(void)arg;
g_pong_id = g_ping_id;
__atomic_store_n(&g_pong_id, *(uint64_t*)arg, __ATOMIC_RELEASE);
u_free(arg);
}
void instance_lite_ping(void) {
struct UASYNC* ua = (struct UASYNC*)__atomic_load_n(&g_ua, __ATOMIC_ACQUIRE);
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)__atomic_load_n(&g_inst, __ATOMIC_ACQUIRE);
if (!ua || !inst) return;
g_ping_id++;
uasync_post(ua, ping_trampoline, NULL);
if (!instance_lite_is_running()) return;
uint64_t* id = u_malloc(sizeof(*id));
if (!id) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "Android health ping allocation failed"); return; }
*id = __atomic_add_fetch(&g_ping_id, 1, __ATOMIC_ACQ_REL);
if (instance_lite_post(ping_trampoline, id, NULL, NULL)) u_free(id);
}
int instance_lite_is_responsive(void) {
struct UASYNC* ua = (struct UASYNC*)__atomic_load_n(&g_ua, __ATOMIC_ACQUIRE);
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)__atomic_load_n(&g_inst, __ATOMIC_ACQUIRE);
if (!ua || !inst || !__atomic_load_n(&g_running, __ATOMIC_ACQUIRE)) return 0;
if (g_ping_id == 0) return 0;
return (g_pong_id == g_ping_id) ? 1 : 0;
uint64_t ping = __atomic_load_n(&g_ping_id, __ATOMIC_ACQUIRE);
return instance_lite_is_running() && ping && __atomic_load_n(&g_pong_id, __ATOMIC_ACQUIRE) == ping;
}
static void collect_conn_list_run(void* unused) {
(void)unused;
chat_core_collect_conn_list_trampoline(instance_lite_get_instance());
}
void instance_lite_collect_conn_list(void) {
struct UASYNC* ua = (struct UASYNC*)__atomic_load_n(&g_ua, __ATOMIC_ACQUIRE);
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)__atomic_load_n(&g_inst, __ATOMIC_ACQUIRE);
if (!ua || !inst) return;
uasync_post(ua, chat_core_collect_conn_list_trampoline, inst);
if (instance_lite_post(collect_conn_list_run, NULL, NULL, NULL))
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "Android connection list request rejected: core not ready");
}
void instance_lite_collect_conn_metrics(uint64_t peer_node_id) {
struct UASYNC* ua = (struct UASYNC*)__atomic_load_n(&g_ua, __ATOMIC_ACQUIRE);
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)__atomic_load_n(&g_inst, __ATOMIC_ACQUIRE);
if (!ua || !inst) return;
struct chat_node_arg* arg = (struct chat_node_arg*)u_malloc(sizeof(struct chat_node_arg));
if (!arg) return;
arg->inst = inst;
if (!arg) { DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Android connection metrics allocation failed"); return; }
arg->node_id = peer_node_id;
uasync_post(ua, chat_core_collect_conn_metrics_trampoline, arg);
if (instance_lite_post(chat_core_collect_conn_metrics_trampoline, arg, &arg->inst, NULL)) u_free(arg);
}
int instance_lite_is_running(void) {
@@ -110,12 +110,12 @@ int instance_lite_configure_radio(int64_t request_id, const int* values, instanc
* При инициализации ставит запрос в очередь; при завершившемся worker делает join/start.
* Обновление асинхронное; старые handles утрачивают силу.
* Stop имеет приоритет перед ожидающим restart. Последний ещё не взятый worker запрос заменяет предыдущий. */
void instance_lite_restart(const char* new_config_text);
int instance_lite_restart(const char* new_config_text); /* 0 — запрос принят, -1 — отказ с логом. */
/* Лёгкое обновление сокетов (auto_sockets=android): перечитывает [server] секции
* из нового конфига и сверяет сокеты без полного рестарта. Posts to uasync thread.
* Если ядро не запущено — только логирует ошибку и дропает вызов. */
void instance_lite_update_sockets(const char* config_text);
* 0 — копия принята worker, -1 — ошибка памяти/неготовое ядро с логом. */
int instance_lite_update_sockets(const char* config_text);
/* Поставить локальный ping в uasync (не сетевой). is_responsive=1 после исполнения последнего ping;
* до первого ping/при остановке возвращает 0. Встроенного ожидания и проверки таймаута нет. */