Separate core, UTUN and chat lifecycles with owned asynchronous work

This commit is contained in:
evgeny committed 2026-09-28 11:39:08 +03:00
1 parent 5838e63cde
commit 72fee9e245
47 files changed
+708 -337

No files matched your search

+141 -119
View File
@@ -52,6 +52,7 @@
// Forward declarations
static uint32_t get_dest_ip(const uint8_t *packet, size_t len);
static int mkdir_recursive(const char* path);
// Global instance for signal handlers
static struct UTUN_INSTANCE *g_instance = NULL;
@@ -193,22 +194,6 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing module created");
if (g_tun_init_enabled && config->global.tun_enabled) {
instance->tun = tun_init(ua, config);
if (!instance->tun) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to initialize TUN device");
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN interface initialized: %s", instance->tun->ifname);
if (config->route_subnets) {
int added = tun_route_add_all(instance->tun->ifindex, instance->tun->ifname, config->route_subnets);
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Added %d system routes for TUN interface", added);
instance->route_subnets = config->route_subnets;
}
} else {
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN initialization disabled - skipping TUN device setup");
instance->tun = NULL;
}
int socket_result = init_sockets(instance);
instance->socket_init_status = socket_result;
if (socket_result != 0) {
@@ -218,17 +203,14 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u
instance->nat_det = nat_detection_create(instance);
if (!instance->nat_det) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NAT detection creation failed"); }
if (config->global.db_path[0]) mkdir_recursive(config->global.db_path);
if (g_topo_group_enabled) {
instance->topo_groups = topo_groups_init(instance);
if (!instance->topo_groups) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to initialize BGP module");
} else {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP module initialized");
topo_group_invite_init(instance);
struct TOPO_GROUP* g = topo_groups_get_default(instance->topo_groups);
if (instance->rt && g && g->local_node) {
topo_group_update_my_nodeinfo(instance, g);
}
}
} else {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "topo_group initialization disabled, skipping BGP module");
@@ -261,12 +243,6 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u
if (g_topo_group_enabled && instance->topo_groups)
etcp_router_bind(instance, ETCP_RT_ID_CONN_MGR, conn_mgr_router_recv_handler);
// Bind DATA handler via etcp_router (after etcp_router_init)
if (routing_bind(instance) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "Failed to bind DATA via etcp_router");
return -1;
}
// TCP proxy server (exit node, optional) — must be before tcp_proxy_client so
// tcp_proxy_client_create can overwrite the handler if it has remote mappings
#ifndef _WIN32
@@ -440,20 +416,8 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
}
DEBUG_INFO(DEBUG_CATEGORY_SYS, "[DESTROY] C done — control_srv");
/* Phase D: config conn handles */
{
struct CONFIG_CONN_HANDLE* ch = instance->config_conn_handles;
int ch_count = 0;
while (ch) {
struct CONFIG_CONN_HANDLE* next = ch->next;
topo_group_peer_close(ch->request);
u_free(ch); ch_count++;
ch = next;
}
if (ch_count) DEBUG_INFO(DEBUG_CATEGORY_SYS, "[DESTROY] D: closed %d config handles", ch_count);
}
instance->config_conn_handles = NULL;
DEBUG_INFO(DEBUG_CATEGORY_SYS, "[DESTROY] D done — config_conn");
chat_service_stop(instance);
utun_service_stop(instance);
/* Phase E: socket_monitor + auto_socket */
socket_monitor_destroy(instance);
@@ -494,18 +458,6 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
instance->etcp_sockets = NULL;
DEBUG_INFO(DEBUG_CATEGORY_SYS, "[DESTROY] G done — ETCP sockets");
/* Phase H: chat */
chat_headless_control_destroy(instance);
chat_media_startup_backfill_destroy(instance);
dm_mailbox_destroy(instance);
dm_core_destroy(instance);
call_headless_destroy(instance);
call_destroy(instance);
radio_headless_destroy(instance);
chat_sync_destroy(instance);
chat_core_destroy(instance);
DEBUG_INFO(DEBUG_CATEGORY_SYS, "[DESTROY] H done — chat");
/* Phase I: db_sync */
db_sync_destroy(instance);
DEBUG_INFO(DEBUG_CATEGORY_SYS, "[DESTROY] I done — db_sync");
@@ -669,7 +621,6 @@ void utun_instance_stop(struct UTUN_INSTANCE *instance) {
instance->running = 0;
// Wakeup main loop using built-in uasync wakeup
if (instance->ua) {
memory_pool_destroy(instance->pkt_pool);
uasync_wakeup(instance->ua);
}
@@ -685,8 +636,9 @@ static int mkdir_recursive(const char *path) {
return utun_mkdir(tmp, 0755);
}
int utun_instance_init(struct UTUN_INSTANCE *instance) {
if (!instance) return -1;
int utun_core_start(struct UTUN_INSTANCE *instance) {
if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "core start: NULL instance"); return -1; }
if (instance->core_started) return 0;
struct utun_config* config = instance->config;
if (config) {
@@ -697,56 +649,11 @@ int utun_instance_init(struct UTUN_INSTANCE *instance) {
instance->keepalive_interval = ka;
}
// db_sync — распределённая таблица с репликацией
db_sync_init(instance);
// Generic replication is a core service; channel instances belong to chat.
if (!instance->db_sync && db_sync_init(instance) < 0) return -1;
// chat — сообщения, каналы, P2P-синхронизация
if (instance->config->global.chatserver_enabled) {
if (!instance->config->global.db_path[0])
snprintf(instance->config->global.db_path, sizeof(instance->config->global.db_path), "/var/lib/utun");
mkdir_recursive(instance->config->global.db_path);
char db_file[512];
snprintf(db_file, sizeof(db_file), "%s/chats.db", instance->config->global.db_path);
if (chat_core_init(instance, db_file) == 0) {
chat_sync_init(instance);
chat_media_backfill(instance);
chat_media_startup_backfill_init(instance);
/* dm_mailbox до dm_core: dm_core_init ставит deliver-cb в mailbox */
dm_mailbox_init(instance);
dm_core_init(instance);
call_init(instance);
}
}
// Set TUN interface in routing module
if (instance->tun) {
routing_set_tun(instance);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "TUN interface registered in routing module");
}
// Initialize NAT (after routing_set_tun, before connections)
if (instance->config->global.nat_enabled) {
int nat_ret = nat_transport_init(instance);
if (nat_ret != 0) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT transport init failed (non-fatal)");
}
}
// Initialize media delivery
if (media_delivery_init(instance) != 0) {
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "media_delivery init failed (non-fatal)");
}
// Initialize media async engine
instance->media_async = media_async_create();
if (!instance->media_async) {
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "media_async create failed");
}
// Note: TUN socket is already registered in tun_init()
// Initialize connections
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "utun_instance_init() calling init_connections() for instance %p", instance);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "core start: initializing transports instance=%p", instance);
int conn_result = init_connections(instance);
@@ -774,6 +681,101 @@ int utun_instance_init(struct UTUN_INSTANCE *instance) {
}
}
// Start the main loop
instance->running = 1;
// Initialize NTP time sync (non-fatal if fails)
if (ntp_time_init(instance) != 0) {
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "NTP time sync init failed (non-fatal)");
}
// Initialize NTP node time exchange (non-fatal)
if (ntp_node_time_init(instance) != 0) {
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "NTP node time exchange init failed (non-fatal)");
}
broadcast_init_instance(instance);
radio_init_instance(instance);
instance->core_started = 1;
DEBUG_INFO(DEBUG_CATEGORY_SYS, "core started");
return 0;
}
/* Service lifecycle runs on the owning uasync thread, outside module callbacks. */
int utun_service_start(struct UTUN_INSTANCE* instance) {
if (!instance || !instance->core_started || !instance->topo_groups) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "UTUN start requires a running core"); return -1;
}
if (instance->utun_started) return 0;
struct utun_config* config = instance->config;
struct UASYNC* ua = instance->ua;
struct TOPO_GROUP* group = topo_groups_create_group(instance->topo_groups, TOPO_GROUP_UTUN, TOPO_GROUP_TYPE_UTUN, NULL);
if (!group) return -1;
instance->conn_mgr = group->conn_mgr;
if (g_tun_init_enabled && config->global.tun_enabled) {
instance->tun = tun_init(ua, config);
if (!instance->tun) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to initialize TUN device");
goto fail;
}
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN interface initialized: %s", instance->tun->ifname);
if (config->route_subnets) {
int added = tun_route_add_all(instance->tun->ifindex, instance->tun->ifname, config->route_subnets);
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Added %d system routes for TUN interface", added);
instance->route_subnets = config->route_subnets;
}
} else {
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN initialization disabled - skipping TUN device setup");
instance->tun = NULL;
}
if (routing_bind(instance) < 0) goto fail;
if (instance->tun) routing_set_tun(instance);
if (config->global.nat_enabled && nat_transport_init(instance) < 0) goto fail;
if (init_client_connections(instance, config->clients) < 0) goto fail;
instance->utun_started = 1;
DEBUG_INFO(DEBUG_CATEGORY_SYS, "UTUN service started"); return 0;
fail:
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "UTUN service start failed; rolling back");
utun_service_stop(instance); return -1;
}
void utun_service_stop(struct UTUN_INSTANCE* instance) {
if (!instance) return;
instance->utun_started = 0;
if (instance->nat_tr.initialized) nat_transport_destroy(instance);
if (instance->router_bindings.callbacks[ETCP_RT_ID_DATA]) etcp_router_unbind(instance, ETCP_RT_ID_DATA);
if (instance->tun) {
if (instance->tun->ifindex && instance->route_subnets)
tun_route_del_all(instance->tun->ifindex, instance->tun->ifname, instance->route_subnets);
tun_close(instance->tun); instance->tun = NULL;
}
instance->route_subnets = NULL;
while (instance->config_conn_handles) {
struct CONFIG_CONN_HANDLE* owner = instance->config_conn_handles;
instance->config_conn_handles = owner->next;
topo_group_peer_close(owner->request); u_free(owner);
}
if (topo_groups_get_default(instance->topo_groups)) topo_groups_remove_group(instance->topo_groups, TOPO_GROUP_UTUN);
DEBUG_INFO(DEBUG_CATEGORY_SYS, "UTUN service stopped");
}
int chat_service_start(struct UTUN_INSTANCE* instance) {
if (!instance || !instance->core_started || !instance->topo_sqlite_db) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "chat start requires a running core with SQLite"); return -1;
}
if (instance->chat_started) return 0;
char db_file[512];
snprintf(db_file, sizeof(db_file), "%s/chats.db", instance->config->global.db_path);
if (db_sync_enable(instance) < 0) return -1;
topo_group_invite_init(instance);
if (chat_core_init(instance, db_file) < 0 || chat_sync_init(instance) < 0) goto fail;
instance->media_async = media_async_create();
if (!instance->media_async || media_delivery_init(instance) < 0) goto fail;
if (dm_mailbox_init(instance) < 0 || dm_core_init(instance) < 0 || call_init(instance) < 0) goto fail;
chat_media_backfill(instance);
chat_media_startup_backfill_init(instance);
// Initialize headless chat control if configured in [chatserver]
if (instance->config->global.headless_control_bind[0] != '\0') {
char ip_str[64] = "127.0.0.1"; int port = 9999;
@@ -816,23 +818,43 @@ int utun_instance_init(struct UTUN_INSTANCE *instance) {
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "Failed to init headless radio audio socket (non-fatal)");
}
// Start the main loop
instance->running = 1;
// Initialize NTP time sync (non-fatal if fails)
if (ntp_time_init(instance) != 0) {
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "NTP time sync init failed (non-fatal)");
instance->chat_started = 1;
DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "chat service started"); return 0;
fail:
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "chat service start failed; rolling back");
chat_service_stop(instance); return -1;
}
void chat_service_stop(struct UTUN_INSTANCE* instance) {
if (!instance) return;
instance->chat_started = 0;
chat_headless_control_destroy(instance);
call_headless_destroy(instance); radio_headless_destroy(instance);
chat_media_startup_backfill_destroy(instance);
media_async_destroy(instance->media_async); instance->media_async = NULL;
media_delivery_destroy(instance);
call_destroy(instance); dm_core_destroy(instance); dm_mailbox_destroy(instance);
etcp_unbind(instance, ETCP_RT_ID_GROUP_INVITE);
topo_group_invite_cancel_all(instance);
chat_sync_destroy(instance);
struct ll_entry* entry = instance->topo_groups ? instance->topo_groups->group_list->head : NULL;
while (entry) {
struct TOPO_GROUP* group = (struct TOPO_GROUP*)entry; entry = entry->next;
if (group->group_type != TOPO_GROUP_TYPE_CHAT) continue;
struct DB_SYNC_INSTANCE* sync = db_sync_instance_find(instance, group->group_id);
if (sync) db_sync_instance_remove(sync);
topo_groups_remove_group(instance->topo_groups, group->group_id);
}
chat_core_destroy(instance);
DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "chat service stopped");
}
// Initialize NTP node time exchange (non-fatal)
if (ntp_node_time_init(instance) != 0) {
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "NTP node time exchange init failed (non-fatal)");
}
broadcast_init_instance(instance);
radio_init_instance(instance);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Connections initialized successfully");
/* Application convenience entry point; embedders may start each service explicitly. */
int utun_instance_init(struct UTUN_INSTANCE* instance) {
if (utun_core_start(instance) < 0) return -1;
if (instance->topo_groups && utun_service_start(instance) < 0) return -1;
if (instance->config->global.chatserver_enabled && chat_service_start(instance) < 0) return -1;
return 0;
}
@@ -1009,7 +1031,7 @@ struct UTUN_INSTANCE *utun_instance_reload(struct UTUN_INSTANCE *instance, struc
}
os = next;
}
int client_result = init_client_connections(instance, new_config->clients);
int client_result = instance->utun_started ? init_client_connections(instance, new_config->clients) : 0;
if (client_result < 0)
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "reload: failed to replace client handles; previous connections retained");
// Reload networks: clear and repopulate