Separate core, UTUN and chat lifecycles with owned asynchronous work
This commit is contained in:
1 parent
5838e63cde
commit
72fee9e245
47 files changed
+708
-337
No files matched your search
+141
-119
@@ -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
|
||||
|
||||
Reference in new issue
Block a user