|
|
|
@ -31,6 +31,8 @@ |
|
|
|
#include <stdlib.h> |
|
|
|
#include <stdlib.h> |
|
|
|
#include <unistd.h> |
|
|
|
#include <unistd.h> |
|
|
|
#include <signal.h> |
|
|
|
#include <signal.h> |
|
|
|
|
|
|
|
#include <time.h> |
|
|
|
|
|
|
|
#include <errno.h> |
|
|
|
#include <openssl/evp.h> |
|
|
|
#include <openssl/evp.h> |
|
|
|
|
|
|
|
|
|
|
|
#ifdef __ANDROID__ |
|
|
|
#ifdef __ANDROID__ |
|
|
|
@ -44,19 +46,33 @@ |
|
|
|
#define IL_LOGE(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__) |
|
|
|
#define IL_LOGE(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__) |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
/* ── Static state ── */ |
|
|
|
/* ── Static state ──
|
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* Thread model: one Kotlin thread, one C worker thread. |
|
|
|
|
|
|
|
* Each variable has exactly ONE writer — no data races by construction. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* Kotlin-writes (signals/commands to C): |
|
|
|
|
|
|
|
* g_stop, g_do_restart, g_restart_config, g_event_handler, g_db_path |
|
|
|
|
|
|
|
* C-writes (state to Kotlin): |
|
|
|
|
|
|
|
* g_ua, g_inst, g_running, g_thread_exited, g_thread_running |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
static struct UASYNC* g_ua = NULL; |
|
|
|
static struct UASYNC* g_ua = NULL; /* C-write, Kotlin-read via __atomic */ |
|
|
|
static struct UTUN_INSTANCE* g_inst = NULL; |
|
|
|
static struct UTUN_INSTANCE* g_inst = NULL; /* C-write, Kotlin-read via __atomic */ |
|
|
|
static pthread_t g_thread; |
|
|
|
static pthread_t g_thread; |
|
|
|
static volatile int g_running = 0; |
|
|
|
static volatile int g_stop = 0; /* Kotlin-write, C-read (poll loop) */ |
|
|
|
static volatile int g_stop = 0; |
|
|
|
static volatile int g_do_restart = 0; /* Kotlin-write, C atomic-xchg→0 */ |
|
|
|
static volatile int g_do_restart = 0; |
|
|
|
static volatile char* g_restart_config = NULL; /* Kotlin-write, C atomic-xchg→NULL (ownership transfer) */ |
|
|
|
static char* g_restart_config = NULL; |
|
|
|
static volatile int g_running = 0; /* C-write, Kotlin-read */ |
|
|
|
static char g_db_path[512]; |
|
|
|
static volatile int g_thread_running = 0; /* C-write + Kotlin CAS-guard for start() */ |
|
|
|
static instance_lite_event_fn g_event_handler = NULL; |
|
|
|
static volatile int g_thread_exited = 0; /* C-write (under mutex), Kotlin-read */ |
|
|
|
|
|
|
|
static volatile int g_generation = 0; /* Kotlin-write, C-read */ |
|
|
|
|
|
|
|
static char g_db_path[512]; /* Kotlin-write (start), C-read (init) */ |
|
|
|
|
|
|
|
static instance_lite_event_fn g_event_handler = NULL; /* Kotlin-write, C-read+call */ |
|
|
|
static char g_generated_pub[65]; |
|
|
|
static char g_generated_pub[65]; |
|
|
|
static char g_generated_priv[65]; |
|
|
|
static char g_generated_priv[65]; |
|
|
|
|
|
|
|
static pthread_mutex_t g_stop_mutex = PTHREAD_MUTEX_INITIALIZER; |
|
|
|
|
|
|
|
static pthread_cond_t g_stop_cond = PTHREAD_COND_INITIALIZER; |
|
|
|
|
|
|
|
|
|
|
|
/* ── Generate X25519 key pair, hex-encode to out buffers ── */ |
|
|
|
/* ── Generate X25519 key pair, hex-encode to out buffers ── */ |
|
|
|
|
|
|
|
|
|
|
|
@ -200,15 +216,16 @@ static void heartbeat_cb(void* arg) { |
|
|
|
|
|
|
|
|
|
|
|
static void* instance_thread(void* arg) { |
|
|
|
static void* instance_thread(void* arg) { |
|
|
|
char* config_text = (char*)arg; |
|
|
|
char* config_text = (char*)arg; |
|
|
|
|
|
|
|
int my_gen = g_generation; |
|
|
|
|
|
|
|
|
|
|
|
struct utun_config* config = parse_config_from_buf(config_text, strlen(config_text), "android"); |
|
|
|
struct utun_config* config = parse_config_from_buf(config_text, strlen(config_text), "android"); |
|
|
|
u_free(config_text); |
|
|
|
u_free(config_text); |
|
|
|
if (!config) { IL_LOGE("parse_config_from_buf failed"); return NULL; } |
|
|
|
if (!config) { IL_LOGE("parse_config_from_buf failed"); __atomic_store_n(&g_thread_running, 0, __ATOMIC_RELEASE); pthread_detach(pthread_self()); return NULL; } |
|
|
|
|
|
|
|
|
|
|
|
install_crash_handlers(); |
|
|
|
install_crash_handlers(); |
|
|
|
|
|
|
|
|
|
|
|
g_ua = uasync_create(); |
|
|
|
g_ua = uasync_create(); |
|
|
|
if (!g_ua) { IL_LOGE("uasync_create failed"); free_config(config); return NULL; } |
|
|
|
if (!g_ua) { IL_LOGE("uasync_create failed"); free_config(config); __atomic_store_n(&g_thread_running, 0, __ATOMIC_RELEASE); pthread_detach(pthread_self()); return NULL; } |
|
|
|
|
|
|
|
|
|
|
|
if (config->global.log_udp_ip[0] && config->global.log_udp_port > 0) { |
|
|
|
if (config->global.log_udp_ip[0] && config->global.log_udp_port > 0) { |
|
|
|
udp_log_set_target(config->global.log_udp_ip, config->global.log_udp_port); |
|
|
|
udp_log_set_target(config->global.log_udp_ip, config->global.log_udp_port); |
|
|
|
@ -225,6 +242,8 @@ static void* instance_thread(void* arg) { |
|
|
|
uasync_destroy(g_ua, 0); |
|
|
|
uasync_destroy(g_ua, 0); |
|
|
|
g_ua = NULL; |
|
|
|
g_ua = NULL; |
|
|
|
u_report_unfreed_blocks(); |
|
|
|
u_report_unfreed_blocks(); |
|
|
|
|
|
|
|
__atomic_store_n(&g_thread_running, 0, __ATOMIC_RELEASE); |
|
|
|
|
|
|
|
pthread_detach(pthread_self()); |
|
|
|
return NULL; |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -257,6 +276,8 @@ static void* instance_thread(void* arg) { |
|
|
|
uasync_destroy(g_ua, 0); |
|
|
|
uasync_destroy(g_ua, 0); |
|
|
|
g_ua = NULL; |
|
|
|
g_ua = NULL; |
|
|
|
u_report_unfreed_blocks(); |
|
|
|
u_report_unfreed_blocks(); |
|
|
|
|
|
|
|
__atomic_store_n(&g_thread_running, 0, __ATOMIC_RELEASE); |
|
|
|
|
|
|
|
pthread_detach(pthread_self()); |
|
|
|
return NULL; |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -282,19 +303,26 @@ static void* instance_thread(void* arg) { |
|
|
|
(const uint8_t*)config->global.my_public_key_hex, 64); |
|
|
|
(const uint8_t*)config->global.my_public_key_hex, 64); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
g_running = 1; |
|
|
|
__atomic_store_n(&g_running, 1, __ATOMIC_RELEASE); |
|
|
|
uasync_mark_running(g_ua); |
|
|
|
uasync_mark_running(g_ua); |
|
|
|
chat_event_post(CHAT_EVT_SERVICE_STARTED, NULL, 0); |
|
|
|
chat_event_post(CHAT_EVT_SERVICE_STARTED, NULL, 0); |
|
|
|
|
|
|
|
|
|
|
|
uasync_set_timeout(g_ua, 100000, NULL, heartbeat_cb, "hb"); |
|
|
|
uasync_set_timeout(g_ua, 100000, NULL, heartbeat_cb, "hb"); |
|
|
|
|
|
|
|
|
|
|
|
while (!g_stop) { |
|
|
|
while (!__atomic_load_n(&g_stop, __ATOMIC_ACQUIRE)) { |
|
|
|
uasync_poll(g_ua, 100); |
|
|
|
uasync_poll(g_ua, 100); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
while (g_do_restart && g_restart_config) { |
|
|
|
while (__atomic_load_n(&g_do_restart, __ATOMIC_ACQUIRE)) { |
|
|
|
IL_LOGI("poll exit: restart requested, destroying old instance"); |
|
|
|
IL_LOGI("poll exit: restart requested, destroying old instance"); |
|
|
|
|
|
|
|
__atomic_store_n(&g_do_restart, 0, __ATOMIC_RELEASE); |
|
|
|
|
|
|
|
IL_LOGI("pre-destroy: g_inst=%p node_id=0x%016llx ua=%p running=%d sockets=%p conns=%p", |
|
|
|
|
|
|
|
g_inst, g_inst ? (unsigned long long)g_inst->node_id : 0ULL, |
|
|
|
|
|
|
|
g_ua, g_running, |
|
|
|
|
|
|
|
g_inst ? g_inst->etcp_sockets : NULL, |
|
|
|
|
|
|
|
g_inst ? g_inst->connections : NULL); |
|
|
|
uasync_mark_stopped(g_ua); |
|
|
|
uasync_mark_stopped(g_ua); |
|
|
|
|
|
|
|
__atomic_store_n(&g_running, 0, __ATOMIC_RELEASE); |
|
|
|
utun_instance_destroy(g_inst); |
|
|
|
utun_instance_destroy(g_inst); |
|
|
|
g_inst = NULL; |
|
|
|
g_inst = NULL; |
|
|
|
uasync_print_resources(g_ua, "AFTER_DESTROY"); |
|
|
|
uasync_print_resources(g_ua, "AFTER_DESTROY"); |
|
|
|
@ -305,10 +333,9 @@ static void* instance_thread(void* arg) { |
|
|
|
|
|
|
|
|
|
|
|
chat_event_set_handler(chat_event_forward); |
|
|
|
chat_event_set_handler(chat_event_forward); |
|
|
|
|
|
|
|
|
|
|
|
char* cfg = g_restart_config; |
|
|
|
char* cfg = (char*)__atomic_exchange_n(&g_restart_config, NULL, __ATOMIC_ACQUIRE); |
|
|
|
g_restart_config = NULL; |
|
|
|
if (!cfg) { IL_LOGE("poll exit: restart_config is NULL"); break; } |
|
|
|
g_do_restart = 0; |
|
|
|
__atomic_store_n(&g_stop, 0, __ATOMIC_RELEASE); |
|
|
|
g_stop = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
IL_LOGI("poll exit: creating new instance from restart config"); |
|
|
|
IL_LOGI("poll exit: creating new instance from restart config"); |
|
|
|
struct utun_config* config = parse_config_from_buf(cfg, strlen(cfg), "android"); |
|
|
|
struct utun_config* config = parse_config_from_buf(cfg, strlen(cfg), "android"); |
|
|
|
@ -338,30 +365,40 @@ static void* instance_thread(void* arg) { |
|
|
|
chat_event_post(CHAT_EVT_KEYS_GENERATED, (const uint8_t*)config->global.my_public_key_hex, 64); |
|
|
|
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"); |
|
|
|
uasync_set_timeout(g_ua, 100000, NULL, heartbeat_cb, "hb"); |
|
|
|
g_running = 1; |
|
|
|
__atomic_store_n(&g_running, 1, __ATOMIC_RELEASE); |
|
|
|
uasync_mark_running(g_ua); |
|
|
|
uasync_mark_running(g_ua); |
|
|
|
chat_event_post(CHAT_EVT_SERVICE_STARTED, NULL, 0); |
|
|
|
chat_event_post(CHAT_EVT_SERVICE_STARTED, NULL, 0); |
|
|
|
|
|
|
|
|
|
|
|
IL_LOGI("poll exit: restart done, entering new poll loop"); |
|
|
|
IL_LOGI("poll exit: restart done, entering new poll loop"); |
|
|
|
while (!g_stop) uasync_poll(g_ua, 100); |
|
|
|
while (!__atomic_load_n(&g_stop, __ATOMIC_ACQUIRE)) uasync_poll(g_ua, 100); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
IL_LOGI("poll exit: final cleanup"); |
|
|
|
IL_LOGI("poll exit: final cleanup"); |
|
|
|
|
|
|
|
|
|
|
|
if (g_inst) { |
|
|
|
if (my_gen == g_generation) { |
|
|
|
utun_instance_destroy(g_inst); |
|
|
|
__atomic_store_n(&g_running, 0, __ATOMIC_RELEASE); |
|
|
|
g_inst = NULL; |
|
|
|
if (g_inst) { |
|
|
|
} |
|
|
|
utun_instance_destroy(g_inst); |
|
|
|
if (g_ua) { |
|
|
|
g_inst = NULL; |
|
|
|
uasync_destroy(g_ua, 0); |
|
|
|
} |
|
|
|
g_ua = NULL; |
|
|
|
if (g_ua) { |
|
|
|
|
|
|
|
uasync_destroy(g_ua, 0); |
|
|
|
|
|
|
|
g_ua = NULL; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
char* stale_cfg = (char*)__atomic_exchange_n(&g_restart_config, NULL, __ATOMIC_ACQUIRE); |
|
|
|
|
|
|
|
u_free(stale_cfg); |
|
|
|
|
|
|
|
u_report_unfreed_blocks(); |
|
|
|
|
|
|
|
chat_event_post(CHAT_EVT_SERVICE_STOPPED, NULL, 0); |
|
|
|
|
|
|
|
IL_LOGI("cleanup complete (gen=%d)", my_gen); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
IL_LOGI("cleanup skipped — thread gen=%d but current gen=%d (stale thread)", my_gen, g_generation); |
|
|
|
} |
|
|
|
} |
|
|
|
if (g_restart_config) { u_free(g_restart_config); g_restart_config = NULL; } |
|
|
|
__atomic_store_n(&g_thread_running, 0, __ATOMIC_RELEASE); |
|
|
|
u_report_unfreed_blocks(); |
|
|
|
pthread_mutex_lock(&g_stop_mutex); |
|
|
|
chat_event_post(CHAT_EVT_SERVICE_STOPPED, NULL, 0); |
|
|
|
g_thread_exited = 1; |
|
|
|
g_event_handler = NULL; |
|
|
|
pthread_cond_signal(&g_stop_cond); |
|
|
|
pthread_detach(pthread_self()); |
|
|
|
pthread_mutex_unlock(&g_stop_mutex); |
|
|
|
IL_LOGI("cleanup complete"); |
|
|
|
IL_LOGI("thread exit signalled"); |
|
|
|
return NULL; |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -369,7 +406,17 @@ static void* instance_thread(void* arg) { |
|
|
|
|
|
|
|
|
|
|
|
int instance_lite_start(const char* config_text) { |
|
|
|
int instance_lite_start(const char* config_text) { |
|
|
|
if (!config_text) return -1; |
|
|
|
if (!config_text) return -1; |
|
|
|
if (g_inst) return 0; |
|
|
|
|
|
|
|
|
|
|
|
/* CAS: prevent double-start race between multiple Kotlin restart calls */ |
|
|
|
|
|
|
|
int expected = 0; |
|
|
|
|
|
|
|
if (!__atomic_compare_exchange_n(&g_thread_running, &expected, 1, 0, |
|
|
|
|
|
|
|
__ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (__atomic_load_n(&g_inst, __ATOMIC_ACQUIRE)) { |
|
|
|
|
|
|
|
__atomic_store_n(&g_thread_running, 0, __ATOMIC_RELEASE); |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
debug_config_init(); |
|
|
|
debug_config_init(); |
|
|
|
debug_set_level(DEBUG_LEVEL_INFO); |
|
|
|
debug_set_level(DEBUG_LEVEL_INFO); |
|
|
|
@ -380,12 +427,15 @@ int instance_lite_start(const char* config_text) { |
|
|
|
cfg_get_val(config_text, "db_path", g_db_path, sizeof(g_db_path)); |
|
|
|
cfg_get_val(config_text, "db_path", g_db_path, sizeof(g_db_path)); |
|
|
|
|
|
|
|
|
|
|
|
char* config_copy = u_strdup(config_text); |
|
|
|
char* config_copy = u_strdup(config_text); |
|
|
|
if (!config_copy) return -1; |
|
|
|
if (!config_copy) { __atomic_store_n(&g_thread_running, 0, __ATOMIC_RELEASE); return -1; } |
|
|
|
|
|
|
|
|
|
|
|
g_stop = 0; |
|
|
|
g_generation++; |
|
|
|
|
|
|
|
g_thread_exited = 0; |
|
|
|
|
|
|
|
__atomic_store_n(&g_stop, 0, __ATOMIC_RELEASE); |
|
|
|
int rc = pthread_create(&g_thread, NULL, instance_thread, config_copy); |
|
|
|
int rc = pthread_create(&g_thread, NULL, instance_thread, config_copy); |
|
|
|
if (rc != 0) { |
|
|
|
if (rc != 0) { |
|
|
|
u_free(config_copy); |
|
|
|
u_free(config_copy); |
|
|
|
|
|
|
|
__atomic_store_n(&g_thread_running, 0, __ATOMIC_RELEASE); |
|
|
|
IL_LOGE("pthread_create failed rc=%d", rc); |
|
|
|
IL_LOGE("pthread_create failed rc=%d", rc); |
|
|
|
return -1; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -395,38 +445,62 @@ int instance_lite_start(const char* config_text) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void instance_lite_stop(void) { |
|
|
|
void instance_lite_stop(void) { |
|
|
|
if (!g_inst) return; |
|
|
|
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)__atomic_load_n(&g_inst, __ATOMIC_ACQUIRE); |
|
|
|
|
|
|
|
if (!inst) return; |
|
|
|
IL_LOGI("stopping..."); |
|
|
|
IL_LOGI("stopping..."); |
|
|
|
uasync_mark_stopped(g_ua); |
|
|
|
|
|
|
|
g_stop = 1; |
|
|
|
__atomic_store_n(&g_stop, 1, __ATOMIC_RELEASE); |
|
|
|
if (g_ua) uasync_wakeup(g_ua); |
|
|
|
struct UASYNC* ua = (struct UASYNC*)__atomic_load_n(&g_ua, __ATOMIC_ACQUIRE); |
|
|
|
for (int i = 0; i < 500 && g_inst != NULL; i++) usleep(10000); |
|
|
|
if (ua) uasync_wakeup(ua); |
|
|
|
if (g_inst) { |
|
|
|
|
|
|
|
IL_LOGE("stop timeout, force cleanup"); |
|
|
|
struct timespec ts; |
|
|
|
g_inst = NULL; |
|
|
|
clock_gettime(CLOCK_REALTIME, &ts); |
|
|
|
g_ua = NULL; |
|
|
|
ts.tv_sec += 2; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int timedout = 0; |
|
|
|
|
|
|
|
pthread_mutex_lock(&g_stop_mutex); |
|
|
|
|
|
|
|
while (!g_thread_exited) { |
|
|
|
|
|
|
|
int rc = pthread_cond_timedwait(&g_stop_cond, &g_stop_mutex, &ts); |
|
|
|
|
|
|
|
if (rc == ETIMEDOUT) { |
|
|
|
|
|
|
|
IL_LOGE("stop timeout (2s), detaching thread and bumping generation to prevent stale writes"); |
|
|
|
|
|
|
|
pthread_detach(g_thread); |
|
|
|
|
|
|
|
g_generation++; |
|
|
|
|
|
|
|
timedout = 1; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
g_running = 0; |
|
|
|
pthread_mutex_unlock(&g_stop_mutex); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!timedout) { |
|
|
|
|
|
|
|
int join_rc = pthread_join(g_thread, NULL); |
|
|
|
|
|
|
|
if (join_rc != 0) IL_LOGE("pthread_join failed rc=%d errno=%d", join_rc, errno); |
|
|
|
|
|
|
|
IL_LOGI("thread joined"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* C-thread cleanup already set g_inst=NULL, g_ua=NULL, g_running=0.
|
|
|
|
|
|
|
|
Reset thread sync state only. */ |
|
|
|
|
|
|
|
g_thread_exited = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void instance_lite_restart(const char* new_config_text) { |
|
|
|
void instance_lite_restart(const char* new_config_text) { |
|
|
|
if (!new_config_text) return; |
|
|
|
if (!new_config_text) return; |
|
|
|
instance_lite_event_fn saved_handler = g_event_handler; |
|
|
|
struct UASYNC* ua = (struct UASYNC*)__atomic_load_n(&g_ua, __ATOMIC_ACQUIRE); |
|
|
|
if (!g_ua || !g_inst || !g_running) { |
|
|
|
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)__atomic_load_n(&g_inst, __ATOMIC_ACQUIRE); |
|
|
|
IL_LOGI("restart: instance dead, doing hard restart via stop+start (saved_handler=%p)", (void*)saved_handler); |
|
|
|
int running = __atomic_load_n(&g_running, __ATOMIC_ACQUIRE); |
|
|
|
if (g_inst) instance_lite_stop(); |
|
|
|
if (!ua || !inst || !running) { |
|
|
|
g_event_handler = saved_handler; |
|
|
|
IL_LOGI("restart: instance dead, doing hard restart via stop+start"); |
|
|
|
|
|
|
|
if (inst) instance_lite_stop(); |
|
|
|
instance_lite_start(new_config_text); |
|
|
|
instance_lite_start(new_config_text); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
IL_LOGI("restart: signaling poll loop to exit"); |
|
|
|
IL_LOGI("restart: signaling poll loop to exit"); |
|
|
|
char* copy = u_strdup(new_config_text); |
|
|
|
char* copy = u_strdup(new_config_text); |
|
|
|
if (!copy) return; |
|
|
|
if (!copy) return; |
|
|
|
if (g_restart_config) u_free(g_restart_config); |
|
|
|
char* old_cfg = (char*)__atomic_exchange_n(&g_restart_config, copy, __ATOMIC_RELEASE); |
|
|
|
g_restart_config = copy; |
|
|
|
u_free(old_cfg); |
|
|
|
g_do_restart = 1; |
|
|
|
__atomic_store_n(&g_do_restart, 1, __ATOMIC_RELEASE); |
|
|
|
g_stop = 1; |
|
|
|
__atomic_store_n(&g_stop, 1, __ATOMIC_RELEASE); |
|
|
|
uasync_wakeup(g_ua); |
|
|
|
uasync_wakeup(ua); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* ── Health check ping ── */ |
|
|
|
/* ── Health check ping ── */ |
|
|
|
@ -440,23 +514,28 @@ static void ping_trampoline(void* arg) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void instance_lite_ping(void) { |
|
|
|
void instance_lite_ping(void) { |
|
|
|
if (!g_ua || !g_inst) 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); |
|
|
|
|
|
|
|
if (!ua || !inst) return; |
|
|
|
g_ping_id++; |
|
|
|
g_ping_id++; |
|
|
|
uasync_post(g_ua, ping_trampoline, NULL); |
|
|
|
uasync_post(ua, ping_trampoline, NULL); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int instance_lite_is_responsive(void) { |
|
|
|
int instance_lite_is_responsive(void) { |
|
|
|
if (!g_ua || !g_inst || !g_running) return 0; |
|
|
|
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; |
|
|
|
if (g_ping_id == 0) return 0; |
|
|
|
return (g_pong_id == g_ping_id) ? 1 : 0; |
|
|
|
return (g_pong_id == g_ping_id) ? 1 : 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int instance_lite_is_running(void) { |
|
|
|
int instance_lite_is_running(void) { |
|
|
|
return (g_inst != NULL && g_running) ? 1 : 0; |
|
|
|
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)__atomic_load_n(&g_inst, __ATOMIC_ACQUIRE); |
|
|
|
|
|
|
|
return (inst != NULL && __atomic_load_n(&g_running, __ATOMIC_ACQUIRE)) ? 1 : 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
struct UASYNC* instance_lite_get_uasync(void) { |
|
|
|
struct UASYNC* instance_lite_get_uasync(void) { |
|
|
|
return g_ua; |
|
|
|
return (struct UASYNC*)__atomic_load_n(&g_ua, __ATOMIC_ACQUIRE); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void regenerate_trampoline(void* arg) { |
|
|
|
static void regenerate_trampoline(void* arg) { |
|
|
|
@ -478,12 +557,14 @@ static void regenerate_trampoline(void* arg) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void instance_lite_regenerate_keys(void) { |
|
|
|
void instance_lite_regenerate_keys(void) { |
|
|
|
if (!g_ua || !g_inst) return; |
|
|
|
struct UASYNC* ua = (struct UASYNC*)__atomic_load_n(&g_ua, __ATOMIC_ACQUIRE); |
|
|
|
uasync_post(g_ua, regenerate_trampoline, NULL); |
|
|
|
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)__atomic_load_n(&g_inst, __ATOMIC_ACQUIRE); |
|
|
|
|
|
|
|
if (!ua || !inst) return; |
|
|
|
|
|
|
|
uasync_post(ua, regenerate_trampoline, NULL); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void instance_lite_set_event_handler(instance_lite_event_fn handler) { |
|
|
|
void instance_lite_set_event_handler(instance_lite_event_fn handler) { |
|
|
|
IL_LOGI("[EVT_DIAG] instance_lite_set_event_handler handler=%p g_running=%d", (void*)handler, g_running); |
|
|
|
IL_LOGI("[EVT_DIAG] instance_lite_set_event_handler handler=%p g_running=%d", (void*)handler, g_running); |
|
|
|
g_event_handler = handler; |
|
|
|
g_event_handler = handler; |
|
|
|
if (g_running) chat_event_set_handler(chat_event_forward); |
|
|
|
if (__atomic_load_n(&g_running, __ATOMIC_ACQUIRE)) chat_event_set_handler(chat_event_forward); |
|
|
|
} |
|
|
|
} |
|
|
|
|