@ -17,18 +17,19 @@
# include "chat/chat_core.h"
# include "chat/chat_sync.h"
# include "chat/chat_event.h"
# include "chat/db_sync.h"
# include "u_async.h"
# include "sqlite3.h"
# include "mem.h"
# include "debug_config.h"
# include "transport_layer/etcp_connections.h"
# include "transport_layer/stcp_link.h"
# include "../jni_bridge/android_udp_log.h"
# include <pthread.h>
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <unistd.h>
# include <signal.h>
# include <openssl/evp.h>
# ifdef __ANDROID__
@ -49,6 +50,8 @@ static struct UTUN_INSTANCE* g_inst = NULL;
static pthread_t g_thread ;
static volatile int g_running = 0 ;
static volatile int g_stop = 0 ;
static volatile int g_do_restart = 0 ;
static char * g_restart_config = NULL ;
static char g_db_path [ 512 ] ;
static instance_lite_event_fn g_event_handler = NULL ;
static char g_generated_pub [ 65 ] ;
@ -148,6 +151,35 @@ static void nodeinfo_event_cb(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE*
g_event_handler ( CHAT_EVT_NODEINFO_UPDATED , data , sizeof ( data ) ) ;
}
/* ── Crash handler ── */
static volatile int g_signal_caught = 0 ;
static volatile int g_signal_num = 0 ;
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 ) ;
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 ) ;
}
static void heartbeat_cb ( void * arg ) {
( void ) arg ;
DEBUG_INFO ( DEBUG_CATEGORY_GENERAL , " heartbeat alive " ) ;
uasync_set_timeout ( g_ua , 100000 , NULL , heartbeat_cb , " hb " ) ;
}
/* ── Thread function ── */
static void * instance_thread ( void * arg ) {
@ -157,6 +189,8 @@ static void* instance_thread(void* arg) {
u_free ( config_text ) ;
if ( ! config ) { IL_LOGE ( " parse_config_from_buf failed " ) ; return NULL ; }
install_crash_handlers ( ) ;
g_ua = uasync_create ( ) ;
if ( ! g_ua ) { IL_LOGE ( " uasync_create failed " ) ; free_config ( config ) ; return NULL ; }
@ -232,13 +266,67 @@ static void* instance_thread(void* arg) {
}
g_running = 1 ;
uasync_mark_running ( g_ua ) ;
chat_event_post ( CHAT_EVT_SERVICE_STARTED , NULL , 0 ) ;
uasync_set_timeout ( g_ua , 100000 , NULL , heartbeat_cb , " hb " ) ;
while ( ! g_stop ) {
uasync_poll ( g_ua , 100 ) ;
}
IL_LOGI ( " poll loop exit, cleaning up " ) ;
while ( g_do_restart & & g_restart_config ) {
IL_LOGI ( " poll exit: restart requested, destroying old instance " ) ;
uasync_mark_stopped ( g_ua ) ;
utun_instance_destroy ( g_inst ) ;
g_inst = NULL ;
uasync_print_resources ( g_ua , " AFTER_DESTROY " ) ;
u_report_unfreed_blocks ( ) ;
uasync_destroy ( g_ua , 0 ) ;
g_ua = uasync_create ( ) ;
if ( ! g_ua ) { IL_LOGE ( " poll exit: uasync_create failed " ) ; break ; }
char * cfg = g_restart_config ;
g_restart_config = NULL ;
g_do_restart = 0 ;
g_stop = 0 ;
IL_LOGI ( " poll exit: creating new instance from restart config " ) ;
struct utun_config * config = parse_config_from_buf ( cfg , strlen ( cfg ) , " android " ) ;
u_free ( cfg ) ;
if ( ! config ) { IL_LOGE ( " poll exit: parse_config failed " ) ; break ; }
g_inst = utun_instance_create_from_config ( g_ua , config ) ;
if ( ! g_inst ) { IL_LOGE ( " poll exit: create_from_config failed " ) ; break ; }
char db_file [ 512 ] ;
snprintf ( db_file , sizeof ( db_file ) , " %s/chats.db " , g_db_path ) ;
int rc = sqlite3_open_v2 ( db_file , & g_inst - > topo_sqlite_db ,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX , NULL ) ;
if ( rc = = SQLITE_OK & & g_inst - > topo_sqlite_db ) {
sqlite3_exec ( g_inst - > topo_sqlite_db , " PRAGMA journal_mode=WAL " , NULL , NULL , NULL ) ;
sqlite3_exec ( g_inst - > topo_sqlite_db , " PRAGMA foreign_keys=ON " , NULL , NULL , NULL ) ;
sqlite3_exec ( g_inst - > topo_sqlite_db , " PRAGMA synchronous=NORMAL " , NULL , NULL , NULL ) ;
topo_node_sqlite_init ( g_inst - > topo_sqlite_db ) ;
} else { IL_LOGE ( " poll exit: SQLite open failed rc=%d " , rc ) ; break ; }
if ( utun_instance_init ( g_inst ) ! = 0 ) { IL_LOGE ( " poll exit: utun_instance_init failed " ) ; break ; }
chat_core_sync_my_addresses ( ) ;
if ( g_inst - > config - > global . name [ 0 ] ) chat_core_update_my_name ( g_inst - > config - > global . name ) ;
etcp_router_bind ( g_inst , ETCP_RT_ID_CHAT_SYNC , NULL ) ;
utun_add_nodeinfo_cbk ( g_inst , nodeinfo_event_cb , NULL ) ;
chat_event_post ( CHAT_EVT_KEYS_GENERATED , ( const uint8_t * ) config - > global . my_public_key_hex , 64 ) ;
uasync_set_timeout ( g_ua , 100000 , NULL , heartbeat_cb , " hb " ) ;
g_running = 1 ;
uasync_mark_running ( g_ua ) ;
chat_event_post ( CHAT_EVT_SERVICE_STARTED , NULL , 0 ) ;
IL_LOGI ( " poll exit: restart done, entering new poll loop " ) ;
while ( ! g_stop ) uasync_poll ( g_ua , 100 ) ;
}
IL_LOGI ( " poll exit: final cleanup " ) ;
if ( g_inst ) {
utun_instance_destroy ( g_inst ) ;
@ -248,6 +336,7 @@ static void* instance_thread(void* arg) {
uasync_destroy ( g_ua , 0 ) ;
g_ua = NULL ;
}
if ( g_restart_config ) { u_free ( g_restart_config ) ; g_restart_config = NULL ; }
u_report_unfreed_blocks ( ) ;
chat_event_post ( CHAT_EVT_SERVICE_STOPPED , NULL , 0 ) ;
g_event_handler = NULL ;
@ -264,6 +353,8 @@ int instance_lite_start(const char* config_text) {
debug_config_init ( ) ;
debug_set_level ( DEBUG_LEVEL_INFO ) ;
debug_set_category_level ( DEBUG_CATEGORY_UASYNC , DEBUG_LEVEL_INFO ) ;
debug_set_category_level ( DEBUG_CATEGORY_MEMORY , DEBUG_LEVEL_INFO ) ;
debug_enable_function_name ( 0 ) ;
cfg_get_val ( config_text , " db_path " , g_db_path , sizeof ( g_db_path ) ) ;
@ -286,6 +377,7 @@ int instance_lite_start(const char* config_text) {
void instance_lite_stop ( void ) {
if ( ! g_inst ) return ;
IL_LOGI ( " stopping... " ) ;
uasync_mark_stopped ( g_ua ) ;
g_stop = 1 ;
if ( g_ua ) uasync_wakeup ( g_ua ) ;
for ( int i = 0 ; i < 500 & & g_inst ! = NULL ; i + + ) usleep ( 10000 ) ;
@ -297,86 +389,6 @@ void instance_lite_stop(void) {
g_running = 0 ;
}
static void restart_trampoline ( void * arg ) {
char * config_text = ( char * ) arg ;
IL_LOGI ( " restart: begin " ) ;
chat_sync_destroy ( g_inst ) ;
chat_core_destroy ( g_inst ) ;
if ( g_inst - > topo_sqlite_db ) {
sqlite3_close ( g_inst - > topo_sqlite_db ) ;
g_inst - > topo_sqlite_db = NULL ;
}
struct utun_config * new_config = parse_config_from_buf ( config_text , strlen ( config_text ) , " android " ) ;
u_free ( config_text ) ;
if ( ! new_config ) {
IL_LOGE ( " restart: parse_config failed " ) ;
g_running = 0 ;
g_inst = NULL ;
return ;
}
free_config ( g_inst - > config ) ;
g_inst - > config = new_config ;
ensure_keys ( new_config ) ;
uint8_t pk_bin [ 32 ] ;
for ( int i = 0 ; i < 32 ; i + + ) { unsigned int b ; sscanf ( new_config - > global . my_public_key_hex + i * 2 , " %2x " , & b ) ; pk_bin [ i ] = ( uint8_t ) b ; }
g_inst - > node_id = sc_derive_node_id_from_pubkey ( pk_bin ) ;
sc_init_local_keys ( & g_inst - > my_keys , new_config - > global . my_public_key_hex , new_config - > global . my_private_key_hex ) ;
if ( new_config - > global . log_udp_ip [ 0 ] & & new_config - > global . log_udp_port > 0 ) {
udp_log_set_target ( new_config - > global . log_udp_ip , new_config - > global . log_udp_port ) ;
uasync_set_timeout ( g_ua , 2000 , NULL , udp_trampoline , " udp_log_flush " ) ;
} else {
udp_log_stop ( ) ;
}
char db_file [ 512 ] ;
snprintf ( db_file , sizeof ( db_file ) , " %s/chats.db " , g_db_path ) ;
int rc = sqlite3_open_v2 ( db_file , & g_inst - > topo_sqlite_db ,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX , NULL ) ;
if ( rc = = SQLITE_OK & & g_inst - > topo_sqlite_db ) {
sqlite3_exec ( g_inst - > topo_sqlite_db , " PRAGMA journal_mode=WAL " , NULL , NULL , NULL ) ;
sqlite3_exec ( g_inst - > topo_sqlite_db , " PRAGMA foreign_keys=ON " , NULL , NULL , NULL ) ;
sqlite3_exec ( g_inst - > topo_sqlite_db , " PRAGMA synchronous=NORMAL " , NULL , NULL , NULL ) ;
topo_node_sqlite_init ( g_inst - > topo_sqlite_db ) ;
IL_LOGI ( " restart: SQLite reopened %s " , db_file ) ;
} else {
IL_LOGE ( " restart: SQLite reopen failed rc=%d " , rc ) ;
g_running = 0 ;
g_inst = NULL ;
return ;
}
/* Clean up old sockets so init_connections reads new config */
while ( g_inst - > etcp_sockets ) etcp_socket_remove ( g_inst - > etcp_sockets ) ;
g_inst - > etcp_sockets = NULL ;
while ( g_inst - > tcp_sockets ) tcp_socket_remove ( g_inst - > tcp_sockets ) ;
g_inst - > tcp_sockets = NULL ;
if ( g_inst - > stcp_server ) { stcp_link_server_destroy ( g_inst - > stcp_server ) ; g_inst - > stcp_server = NULL ; }
g_inst - > socket_init_status = 0 ;
if ( utun_instance_init ( g_inst ) ! = 0 ) {
IL_LOGE ( " restart: utun_instance_init failed " ) ;
g_running = 0 ;
g_inst = NULL ;
return ;
}
chat_core_sync_my_addresses ( ) ;
if ( g_inst - > config - > global . name [ 0 ] ) {
chat_core_update_my_name ( g_inst - > config - > global . name ) ;
IL_LOGI ( " restart: my_name set to '%s' " , g_inst - > config - > global . name ) ;
}
IL_LOGI ( " restart: done " ) ;
}
void instance_lite_restart ( const char * new_config_text ) {
if ( ! new_config_text ) return ;
if ( ! g_ua | | ! g_inst | | ! g_running ) {
@ -385,10 +397,14 @@ void instance_lite_restart(const char* new_config_text) {
instance_lite_start ( new_config_text ) ;
return ;
}
IL_LOGI ( " restart: posting to uasync " ) ;
IL_LOGI ( " restart: signaling poll loop to exit " ) ;
char * copy = u_strdup ( new_config_text ) ;
if ( ! copy ) return ;
uasync_post ( g_ua , restart_trampoline , copy ) ;
if ( g_restart_config ) u_free ( g_restart_config ) ;
g_restart_config = copy ;
g_do_restart = 1 ;
g_stop = 1 ;
uasync_wakeup ( g_ua ) ;
}
/* ── Health check ping ── */