dm: прямой обмен сообщениями (DM) + soundtouch + архитектура звонков

- src/dm/: dm_core/dm_crypto/dm_mailbox — прямой обмен сообщениями (DM)
- lib/soundtouch-master/: vendored SoundTouch (изменение темпа/высоты аудио)
- doc/dm_arch.md, doc/chat_call_arch.md: архитектура DM и звонков
- tests: test_dm.c, test_dm_e2e.c
- chat: рефакторинг chat_* модулей, member_sync, invite_build
- chatgui/chatgui-android: синхронизация с новым API чата
- lib/u_async, debug_config, utun_instance: сопутствующие изменения
This commit is contained in:
evgeny committed 2026-09-15 21:09:17 +03:00
1 parent 761f64cf76
commit 73fe99b33f
123 files changed
+14095 -1270

No files matched your search

@@ -4,6 +4,7 @@
#include "headless_control.h"
#include "../libutun_lite/utun_config_api.h"
#include "../libutun_lite/invite_link_c.h"
#include "../libutun_lite/instance_lite.h"
#include "../../../src/chat/chat_setting.h"
#include "../../../src/chat/chat_sync.h"
#include "../../../lib/debug_config.h"
@@ -315,7 +316,13 @@ static int handle_join(int fd, int id, const char* json) {
}
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "join: calling chat_sync_connect_from_invite...");
chat_sync_connect_from_invite(d.channelId, d.nodeId, d.pubkey, addrs_buf, d.addrCount, addrs_len, d.password, d.join_key);
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
if (!inst) {
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "join: no instance running — drop");
send_response(fd, id, NULL, "instance not running");
return 0;
}
chat_sync_connect_from_invite(inst, d.channelId, d.nodeId, d.pubkey, addrs_buf, d.addrCount, addrs_len, d.password, d.join_key);
char resp[512];
snprintf(resp, sizeof(resp),
@@ -331,7 +338,9 @@ static int handle_chat_setting(int fd, int id, const char* json) {
send_response(fd, id, NULL, "missing name or value");
return 0;
}
int rc = chat_setting_set(name, value);
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
if (!inst) { send_response(fd, id, NULL, "instance not running"); return 0; }
int rc = chat_setting_set(inst, name, value);
if (rc == -1) { send_response(fd, id, NULL, "unknown setting"); return 0; }
if (rc == -2) { send_response(fd, id, NULL, "invalid value"); return 0; }
printf("control: chat_setting %s=%s\n", name, value);
@@ -190,6 +190,7 @@ void utun_bridge_send_message(const char* channel_id, const char* text) {
struct chat_msg_submit* req = u_calloc(1, sizeof(struct chat_msg_submit) + datalen);
if (!req) return;
req->inst = instance_lite_get_instance();
strncpy(req->channel_id, channel_id, sizeof(req->channel_id) - 1);
strncpy(req->content_type, "text/plain", sizeof(req->content_type) - 1);
req->data = (uint8_t*)(req + 1);
@@ -206,9 +207,11 @@ void utun_bridge_create_channel(const char* name, const char* channel_id) {
if (!name || !name[0]) { bridge_log(BLEV_ERROR, "create_channel: empty name"); return; }
struct UASYNC* ua = instance_lite_get_uasync();
if (!uasync_is_running(ua)) { bridge_log(BLEV_ERROR, "create_channel: uasync not running"); return; }
char* n = u_strdup(name);
if (!n) { bridge_log(BLEV_ERROR, "create_channel: OOM"); return; }
uasync_post(ua, chat_core_create_channel_auto_trampoline, n);
struct chat_create_auto_arg* req = u_calloc(1, sizeof(*req));
if (!req) { bridge_log(BLEV_ERROR, "create_channel: OOM"); return; }
req->inst = instance_lite_get_instance();
snprintf(req->name, sizeof(req->name), "%s", name);
uasync_post(ua, chat_core_create_channel_auto_trampoline, req);
}
void utun_bridge_connect_node(const char* address, int port, const char* pubkey_hex) {
@@ -235,7 +238,13 @@ void utun_bridge_join_channel(uint64_t channel_id, uint64_t node_id,
utun_bridge_event(6, "{\"node_id\":0,\"result\":-7}"); /* CHAT_EVT_CONNECT_RESULT */
return;
}
chat_sync_connect_from_invite(channel_id, real_node_id, pubkey_bin, addrs_data, addr_count, addrs_data_len, NULL, join_key);
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
if (!inst) {
bridge_log(BLEV_ERROR, "join: instance not running");
utun_bridge_event(6, "{\"node_id\":0,\"result\":-7}"); /* CHAT_EVT_CONNECT_RESULT */
return;
}
chat_sync_connect_from_invite(inst, channel_id, real_node_id, pubkey_bin, addrs_data, addr_count, addrs_data_len, NULL, join_key);
}
void utun_bridge_request_invite_candidates(const char* channel_id) {
@@ -245,10 +254,12 @@ void utun_bridge_request_invite_candidates(const char* channel_id) {
}
struct UASYNC* ua = instance_lite_get_uasync();
if (!uasync_is_running(ua)) { bridge_log(BLEV_ERROR, "request_invite_candidates: uasync not running"); return; }
char* cid = u_strdup(channel_id);
if (!cid) { bridge_log(BLEV_ERROR, "request_invite_candidates: OOM"); return; }
struct chat_invite_cand_req* req = u_calloc(1, sizeof(*req));
if (!req) { bridge_log(BLEV_ERROR, "request_invite_candidates: OOM"); return; }
req->inst = instance_lite_get_instance();
snprintf(req->ch_id, sizeof(req->ch_id), "%s", channel_id);
bridge_log(BLEV_INFO, "request_invite_candidates ch=%s", channel_id);
uasync_post(ua, chat_invite_candidates_trampoline, cid);
uasync_post(ua, chat_invite_candidates_trampoline, req);
}
void utun_bridge_request_invite_link(const char* channel_id, uint64_t target_node_id) {
@@ -260,6 +271,7 @@ void utun_bridge_request_invite_link(const char* channel_id, uint64_t target_nod
if (!uasync_is_running(ua)) { bridge_log(BLEV_ERROR, "request_invite_link: uasync not running"); return; }
struct chat_invite_build_req* req = u_calloc(1, sizeof(*req));
if (!req) { bridge_log(BLEV_ERROR, "request_invite_link: OOM"); return; }
req->inst = instance_lite_get_instance();
strncpy(req->ch_id, channel_id, sizeof(req->ch_id) - 1);
req->target_node_id = target_node_id;
bridge_log(BLEV_INFO, "request_invite_link ch=%s target=0x%016llx",
@@ -274,10 +286,12 @@ void utun_bridge_connect_channel(const char* channel_id) {
}
struct UASYNC* ua = instance_lite_get_uasync();
if (!uasync_is_running(ua)) return;
char* cid = u_strdup(channel_id);
if (!cid) return;
struct chat_create_auto_arg* req = u_calloc(1, sizeof(*req));
if (!req) return;
req->inst = instance_lite_get_instance();
snprintf(req->name, sizeof(req->name), "%s", channel_id);
bridge_log(BLEV_INFO, "connect_channel ch=%s", channel_id);
uasync_post(ua, chat_core_connect_channel_trampoline, cid);
uasync_post(ua, chat_core_connect_channel_trampoline, req);
}
void utun_bridge_connect_member_node(const char* channel_id, uint64_t node_id) {
@@ -290,6 +304,7 @@ void utun_bridge_connect_member_node(const char* channel_id, uint64_t node_id) {
if (!uasync_is_running(ua)) return;
struct chat_connect_node_req* req = u_calloc(1, sizeof(*req));
if (!req) { bridge_log(BLEV_ERROR, "connect_member_node: OOM"); return; }
req->inst = instance_lite_get_instance();
strncpy(req->ch_id, channel_id, sizeof(req->ch_id) - 1);
req->node_id = node_id;
bridge_log(BLEV_INFO, "connect_member_node ch=%s node=0x%016llx", channel_id, (unsigned long long)node_id);
@@ -391,10 +406,11 @@ static void bridge_format_preview_body(const char* ct, const char* d, char* out,
}
char* utun_bridge_get_channels_json(void) {
sqlite3* db = chat_core_get_db();
if (!chat_core_is_initialized() || !db) {
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
sqlite3* db = chat_core_get_db(inst);
if (!chat_core_is_initialized(inst) || !db) {
bridge_log(BLEV_WARN, "get_channels: not initialized db=%p initialized=%d",
(void*)db, chat_core_is_initialized());
(void*)db, chat_core_is_initialized(inst));
return u_strdup("[]");
}
size_t cap = 4096, pos = 1;
@@ -445,8 +461,7 @@ char* utun_bridge_get_channels_json(void) {
}
int online = 0;
uint64_t my_id = g_cc.my_node_id;
struct UTUN_INSTANCE* inst = chat_core_get_inst();
uint64_t my_id = inst->node_id;
sqlite3_stmt* pc = NULL;
snprintf(sql, sizeof(sql), "SELECT DISTINCT node_id FROM \"%s\"", tbl_peers);
if (sqlite3_prepare_v2(db, sql, -1, &pc, NULL) == SQLITE_OK) {
@@ -480,9 +495,10 @@ char* utun_bridge_get_channels_json(void) {
}
char* utun_bridge_get_messages_json(const char* channel_id, int limit) {
sqlite3* db = chat_core_get_db();
if (!chat_core_is_initialized() || !db || !channel_id || !channel_id[0]) return u_strdup("[]");
uint64_t my_id = g_cc.my_node_id;
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
sqlite3* db = chat_core_get_db(inst);
if (!chat_core_is_initialized(inst) || !db || !channel_id || !channel_id[0]) return u_strdup("[]");
uint64_t my_id = inst->node_id;
char tbl_msg[80];
msg_table_name(channel_id, tbl_msg, sizeof(tbl_msg));
@@ -580,7 +596,8 @@ char* utun_bridge_get_messages_json(const char* channel_id, int limit) {
}
uint64_t utun_bridge_get_my_node_id(void) {
return chat_core_is_initialized() ? g_cc.my_node_id : 0;
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
return (inst && chat_core_is_initialized(inst)) ? inst->node_id : 0;
}
void utun_bridge_set_debug_level(const char* category, const char* level) {
@@ -594,11 +611,14 @@ void utun_bridge_set_debug_level(const char* category, const char* level) {
}
void utun_bridge_chat_setting_set(const char* name, const char* value) {
chat_core_set_setting(name, value);
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
if (!inst) { bridge_log(BLEV_ERROR, "chat_setting_set: instance not running"); return; }
chat_core_set_setting(inst, name, value);
}
int utun_bridge_chat_setting_get_int(const char* name, int def) {
return chat_setting_get_int(name, def);
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
return chat_setting_get_int(inst, name, def);
}
/* ──────────────────────────────────────────────────────────────────
@@ -610,8 +630,9 @@ int utun_bridge_chat_setting_get_int(const char* name, int def) {
#include <openssl/evp.h>
char* utun_bridge_get_channel_members_json(const char* channel_id) {
sqlite3* db = chat_core_get_db();
if (!chat_core_is_initialized() || !db || !channel_id || !channel_id[0]) return u_strdup("[]");
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
sqlite3* db = chat_core_get_db(inst);
if (!chat_core_is_initialized(inst) || !db || !channel_id || !channel_id[0]) return u_strdup("[]");
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
@@ -644,7 +665,7 @@ char* utun_bridge_get_channel_members_json(const char* channel_id) {
if (!json) { sqlite3_finalize(st); return u_strdup("[]"); }
json[0] = '[';
uint64_t my_id = g_cc.my_node_id;
uint64_t my_id = inst->node_id;
int first = 1;
while (sqlite3_step(st) == SQLITE_ROW) {
@@ -702,8 +723,9 @@ char* utun_bridge_get_channel_members_json(const char* channel_id) {
}
char* utun_bridge_get_member_detail_json(const char* channel_id, uint64_t node_id) {
sqlite3* db = chat_core_get_db();
if (!chat_core_is_initialized() || !db || !channel_id || node_id == 0) return u_strdup("{}");
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
sqlite3* db = chat_core_get_db(inst);
if (!chat_core_is_initialized(inst) || !db || !channel_id || node_id == 0) return u_strdup("{}");
char buf[8192];
size_t pos = 0;
@@ -774,8 +796,9 @@ char* utun_bridge_get_member_detail_json(const char* channel_id, uint64_t node_i
}
void utun_bridge_set_member_nick(const char* channel_id, uint64_t node_id, const char* nick) {
sqlite3* db = chat_core_get_db();
if (!chat_core_is_initialized() || !db || !channel_id || node_id == 0) return;
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
sqlite3* db = chat_core_get_db(inst);
if (!chat_core_is_initialized(inst) || !db || !channel_id || node_id == 0) return;
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
char sql[256];
@@ -796,6 +819,7 @@ void utun_bridge_set_member_nick(const char* channel_id, uint64_t node_id, const
}
struct member_tags_edit_req {
struct UTUN_INSTANCE* inst;
char ch_id[64];
uint64_t node_id;
int admin, supernode, moder, storage, verified, deleted;
@@ -803,8 +827,8 @@ struct member_tags_edit_req {
static void member_tags_edit_trampoline(void* arg) {
struct member_tags_edit_req* req = (struct member_tags_edit_req*)arg;
if (!req) return;
struct chat_member_tags* t = chat_member_tags_load(req->ch_id, req->node_id);
if (!req || !req->inst) { u_free(arg); return; }
struct chat_member_tags* t = chat_member_tags_load(req->inst, req->ch_id, req->node_id);
if (!t) {
bridge_log(BLEV_ERROR, "set_member_flags: tags_load failed ch=%s nid=0x%016llx",
req->ch_id, (unsigned long long)req->node_id);
@@ -817,7 +841,7 @@ static void member_tags_edit_trampoline(void* arg) {
chat_member_tags_set(t, "storage", req->storage ? "yes" : "no");
chat_member_tags_set(t, "verified", req->verified ? "yes" : "no");
chat_member_tags_set(t, "deleted", req->deleted ? "yes" : "no");
int rc = chat_member_tags_commit(t);
int rc = chat_member_tags_commit(req->inst, t);
bridge_log(rc == 0 ? BLEV_INFO : BLEV_ERROR,
"set_member_flags ch=%s nid=0x%016llx admin=%d super=%d moder=%d storage=%d verified=%d deleted=%d rc=%d",
req->ch_id, (unsigned long long)req->node_id, req->admin, req->supernode,
@@ -827,8 +851,9 @@ static void member_tags_edit_trampoline(void* arg) {
}
int utun_bridge_is_channel_owner(const char* channel_id) {
sqlite3* db = chat_core_get_db();
if (!chat_core_is_initialized() || !db || !channel_id || !channel_id[0]) return 0;
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
sqlite3* db = chat_core_get_db(inst);
if (!chat_core_is_initialized(inst) || !db || !channel_id || !channel_id[0]) return 0;
uint8_t priv[32];
return topo_node_sqlite_channel_get_priv(db, channel_id, priv) == 0 ? 1 : 0;
}
@@ -841,6 +866,7 @@ void utun_bridge_set_member_flags(const char* channel_id, uint64_t node_id,
if (!uasync_is_running(ua)) { bridge_log(BLEV_ERROR, "set_member_flags: uasync not running"); return; }
struct member_tags_edit_req* req = u_calloc(1, sizeof(*req));
if (!req) { bridge_log(BLEV_ERROR, "set_member_flags: OOM"); return; }
req->inst = instance_lite_get_instance();
strncpy(req->ch_id, channel_id, sizeof(req->ch_id) - 1);
req->node_id = node_id;
req->admin = admin; req->supernode = supernode; req->moder = moder;
@@ -851,7 +877,7 @@ void utun_bridge_set_member_flags(const char* channel_id, uint64_t node_id,
}
char* utun_bridge_get_member_links_json(uint64_t node_id) {
struct UTUN_INSTANCE* inst = chat_core_get_inst();
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
if (!inst || node_id == 0) return u_strdup("{}");
struct ETCP_CONN* conn = instance_find_conn(inst, node_id);
@@ -916,6 +942,7 @@ void utun_bridge_transfer_admin(const char* channel_id, uint64_t node_id) {
if (!uasync_is_running(ua)) { bridge_log(BLEV_ERROR, "transfer_admin: uasync not running"); return; }
struct chat_transfer_admin_req* req = u_calloc(1, sizeof(*req));
if (!req) { bridge_log(BLEV_ERROR, "transfer_admin: OOM"); return; }
req->inst = instance_lite_get_instance();
strncpy(req->ch_id, channel_id, sizeof(req->ch_id) - 1);
req->target_node_id = node_id;
uasync_post(ua, chat_core_transfer_admin_trampoline, req);
@@ -932,6 +959,7 @@ void utun_bridge_delete_channel(const char* channel_id) {
if (!uasync_is_running(ua)) { bridge_log(BLEV_ERROR, "delete_channel: uasync not running"); return; }
struct chat_delete_channel_req* req = u_calloc(1, sizeof(*req));
if (!req) { bridge_log(BLEV_ERROR, "delete_channel: OOM"); return; }
req->inst = instance_lite_get_instance();
strncpy(req->ch_id, channel_id, sizeof(req->ch_id) - 1);
uasync_post(ua, chat_core_delete_channel_trampoline, req);
bridge_log(BLEV_INFO, "delete_channel queued ch=%s", channel_id);
@@ -944,8 +972,8 @@ void utun_bridge_delete_channel(const char* channel_id) {
#include "../../../lib/socket_compat.h"
char* utun_bridge_get_local_sockets_json(void) {
if (!chat_core_is_initialized() || !g_cc.inst) return u_strdup("[]");
struct UTUN_INSTANCE* inst = g_cc.inst;
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
if (!inst || !chat_core_is_initialized(inst)) return u_strdup("[]");
size_t cap = 4096, pos = 1;
char* json = u_malloc(cap);
@@ -1049,14 +1077,15 @@ char* utun_bridge_get_local_sockets_json(void) {
}
void utun_bridge_on_network_change(void) {
if (!g_cc.inst) return;
auto_socket_on_network_change(g_cc.inst);
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
if (!inst) return;
auto_socket_on_network_change(inst);
bridge_log(BLEV_DEBUG, "network change notified to auto_socket");
}
static void bridge_set_active_trampoline(void* arg) {
int active = (int)(intptr_t)arg;
struct UTUN_INSTANCE* inst = chat_core_get_inst();
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
if (!inst) return;
utun_set_client_activity(inst, active);
standby_set_enabled(inst, !active);
@@ -1181,6 +1210,7 @@ int utun_bridge_attachment_download(const char* channel_id, int64_t msg_id) {
if (!uasync_is_running(ua)) return -1;
struct attachment_dl_req* req = u_calloc(1, sizeof(*req));
if (!req) return -1;
req->inst = instance_lite_get_instance();
snprintf(req->channel_id, sizeof(req->channel_id), "%s", channel_id);
req->msg_id = msg_id;
bridge_log(BLEV_INFO, "attachment_download: ch=%s msg_id=%lld", channel_id, (long long)msg_id);
@@ -1678,7 +1708,8 @@ JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeSetChatSetting(
(void)thiz;
const char* n = (*env)->GetStringUTFChars(env, name, NULL);
const char* v = (*env)->GetStringUTFChars(env, value, NULL);
chat_core_set_setting(n, v);
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
if (inst) chat_core_set_setting(inst, n, v);
if (v) (*env)->ReleaseStringUTFChars(env, value, v);
if (n) (*env)->ReleaseStringUTFChars(env, name, n);
}
@@ -1687,7 +1718,8 @@ JNIEXPORT jint JNICALL Java_com_utun_chat_data_NativeLib_nativeGetChatSettingInt
JNIEnv* env, jobject thiz, jstring name, jint def) {
(void)thiz;
const char* n = (*env)->GetStringUTFChars(env, name, NULL);
int val = chat_setting_get_int(n, (int)def);
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
int val = chat_setting_get_int(inst, n, (int)def);
if (n) (*env)->ReleaseStringUTFChars(env, name, n);
return (jint)val;
}
@@ -66,6 +66,7 @@ int attachment_send(const char* channel_id, const char* src_file_path, const cha
struct chat_msg_submit* req = u_calloc(1, sizeof(struct chat_msg_submit) + datalen + 1);
if (!req) return -1;
req->inst = instance_lite_get_instance();
snprintf(req->channel_id, sizeof(req->channel_id), "%s", channel_id);
snprintf(req->content_type, sizeof(req->content_type), "application/octet-stream");
snprintf(req->media_src, sizeof(req->media_src), "%s", src_file_path);
@@ -158,7 +158,8 @@ static void cfg_get_val(const char* text, const char* key, char* out, size_t out
/* ── Chat event forward → Kotlin callback ── */
static void chat_event_forward(int type, const uint8_t* data, int len) {
static void chat_event_forward(struct UTUN_INSTANCE* inst, int type, const uint8_t* data, int len) {
(void)inst;
if (g_event_handler) { g_event_handler(type, data, len); return; }
IL_LOGE("[EVT_DIAG] chat_event_forward: g_event_handler is NULL! type=%d len=%d", type, len);
}
@@ -271,7 +272,6 @@ static void* instance_thread(void* arg) {
}
IL_LOGI("[EVT_DIAG] thread: about to set chat_event handler, g_event_handler=%p", (void*)g_event_handler);
chat_event_set_handler(chat_event_forward);
ensure_keys(config);
g_inst = utun_instance_create_from_config(g_ua, config);
@@ -285,6 +285,7 @@ static void* instance_thread(void* arg) {
pthread_detach(pthread_self());
return NULL;
}
chat_event_set_handler(g_inst, chat_event_forward);
IL_LOGI("instance created, node_id=0x%016llx", (unsigned long long)g_inst->node_id);
@@ -322,7 +323,7 @@ static void* instance_thread(void* arg) {
}
/* sockets are now created — sync my addresses to DB */
chat_core_sync_my_addresses();
chat_core_sync_my_addresses(g_inst);
fire_local_sockets_event();
/* Bind chat sync via etcp_router */
@@ -333,7 +334,7 @@ static void* instance_thread(void* arg) {
/* Set my_name from config */
if (g_inst->config->global.name[0]) {
chat_core_update_my_name(g_inst->config->global.name);
chat_core_update_my_name(g_inst, g_inst->config->global.name);
IL_LOGI("my_name set to '%s'", g_inst->config->global.name);
}
@@ -341,13 +342,13 @@ static void* instance_thread(void* arg) {
/* Notify GUI of current public key (initial generation or existing) */
if (strlen(config->global.my_public_key_hex) == 64) {
chat_event_post(CHAT_EVT_KEYS_GENERATED,
chat_event_post(g_inst, CHAT_EVT_KEYS_GENERATED,
(const uint8_t*)config->global.my_public_key_hex, 64);
}
__atomic_store_n(&g_running, 1, __ATOMIC_RELEASE);
uasync_mark_running(g_ua);
chat_event_post(CHAT_EVT_SERVICE_STARTED, NULL, 0);
chat_event_post(g_inst, CHAT_EVT_SERVICE_STARTED, NULL, 0);
while (!__atomic_load_n(&g_stop, __ATOMIC_ACQUIRE)) {
uasync_poll(g_ua, -1);
@@ -373,8 +374,6 @@ static void* instance_thread(void* arg) {
if (!g_ua) { IL_LOGE("poll exit: uasync_create failed"); break; }
standby_init(g_ua);
chat_event_set_handler(chat_event_forward);
char* cfg = (char*)__atomic_exchange_n(&g_restart_config, NULL, __ATOMIC_ACQUIRE);
if (!cfg) { IL_LOGE("poll exit: restart_config is NULL"); break; }
__atomic_store_n(&g_stop, 0, __ATOMIC_RELEASE);
@@ -386,6 +385,7 @@ static void* instance_thread(void* arg) {
g_inst = utun_instance_create_from_config(g_ua, config);
if (!g_inst) { IL_LOGE("poll exit: create_from_config failed"); break; }
chat_event_set_handler(g_inst, chat_event_forward);
char db_file[512];
snprintf(db_file, sizeof(db_file), "%s/chats.db", g_db_path);
@@ -399,17 +399,17 @@ static void* instance_thread(void* arg) {
} 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();
chat_core_sync_my_addresses(g_inst);
fire_local_sockets_event();
if (g_inst->config->global.name[0]) chat_core_update_my_name(g_inst->config->global.name);
if (g_inst->config->global.name[0]) chat_core_update_my_name(g_inst, 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);
etcp_add_link_status_cbk(g_inst, on_link_status_changed, NULL);
chat_event_post(CHAT_EVT_KEYS_GENERATED, (const uint8_t*)config->global.my_public_key_hex, 64);
utun_add_nodeinfo_cbk(g_inst, nodeinfo_event_cb, NULL);
etcp_add_link_status_cbk(g_inst, on_link_status_changed, NULL);
chat_event_post(g_inst, CHAT_EVT_KEYS_GENERATED, (const uint8_t*)config->global.my_public_key_hex, 64);
__atomic_store_n(&g_running, 1, __ATOMIC_RELEASE);
uasync_mark_running(g_ua);
chat_event_post(CHAT_EVT_SERVICE_STARTED, NULL, 0);
chat_event_post(g_inst, CHAT_EVT_SERVICE_STARTED, NULL, 0);
IL_LOGI("poll exit: restart done, entering new poll loop");
while (!__atomic_load_n(&g_stop, __ATOMIC_ACQUIRE)) uasync_poll(g_ua, -1);
@@ -420,6 +420,7 @@ static void* instance_thread(void* arg) {
if (my_gen == g_generation) {
__atomic_store_n(&g_running, 0, __ATOMIC_RELEASE);
if (g_inst) {
chat_event_post(g_inst, CHAT_EVT_SERVICE_STOPPED, NULL, 0);
utun_instance_destroy(g_inst);
g_inst = NULL;
}
@@ -431,7 +432,6 @@ static void* instance_thread(void* arg) {
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);
@@ -662,16 +662,19 @@ int instance_lite_is_responsive(void) {
void instance_lite_collect_conn_list(void) {
struct UASYNC* ua = (struct UASYNC*)__atomic_load_n(&g_ua, __ATOMIC_ACQUIRE);
if (!ua) return;
uasync_post(ua, chat_core_collect_conn_list_trampoline, NULL);
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);
}
void instance_lite_collect_conn_metrics(uint64_t peer_node_id) {
struct UASYNC* ua = (struct UASYNC*)__atomic_load_n(&g_ua, __ATOMIC_ACQUIRE);
if (!ua) return;
uint64_t* arg = (uint64_t*)u_malloc(sizeof(uint64_t));
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 = peer_node_id;
arg->inst = inst;
arg->node_id = peer_node_id;
uasync_post(ua, chat_core_collect_conn_metrics_trampoline, arg);
}
@@ -684,6 +687,10 @@ struct UASYNC* instance_lite_get_uasync(void) {
return (struct UASYNC*)__atomic_load_n(&g_ua, __ATOMIC_ACQUIRE);
}
struct UTUN_INSTANCE* instance_lite_get_instance(void) {
return (struct UTUN_INSTANCE*)__atomic_load_n(&g_inst, __ATOMIC_ACQUIRE);
}
static void regenerate_trampoline(void* arg) {
(void)arg;
if (!g_inst || !g_inst->config) { IL_LOGE("regenerate_keys: instance not running"); return; }
@@ -698,7 +705,7 @@ static void regenerate_trampoline(void* arg) {
g_inst->node_id = sc_derive_node_id_from_pubkey(pk_bin);
sc_init_local_keys(&g_inst->my_keys, g_generated_pub, g_generated_priv);
IL_LOGI("keys regenerated, node_id=0x%016llx", (unsigned long long)g_inst->node_id);
chat_event_post(CHAT_EVT_KEYS_GENERATED, (const uint8_t*)g_generated_pub, 64);
chat_event_post(g_inst, CHAT_EVT_KEYS_GENERATED, (const uint8_t*)g_generated_pub, 64);
}
}
@@ -712,5 +719,8 @@ void instance_lite_regenerate_keys(void) {
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);
g_event_handler = handler;
if (__atomic_load_n(&g_running, __ATOMIC_ACQUIRE)) chat_event_set_handler(chat_event_forward);
if (__atomic_load_n(&g_running, __ATOMIC_ACQUIRE)) {
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)__atomic_load_n(&g_inst, __ATOMIC_ACQUIRE);
if (inst) chat_event_set_handler(inst, chat_event_forward);
}
}
@@ -15,6 +15,7 @@ extern "C" {
#endif
struct UASYNC;
struct UTUN_INSTANCE;
/* Start full uTun stack from INI config text.
* Creates a pthread for uasync event loop.
@@ -32,6 +33,9 @@ int instance_lite_is_running(void);
/* Get current uasync handle (NULL if not running). */
struct UASYNC* instance_lite_get_uasync(void);
/* Get current UTUN_INSTANCE handle (NULL if not running). */
struct UTUN_INSTANCE* instance_lite_get_instance(void);
/* Set Kotlin callback for chat events (CONNECT_RESULT, MSG_RECEIVED, ...) */
typedef void (*instance_lite_event_fn)(int type, const uint8_t* data, int len);
void instance_lite_set_event_handler(instance_lite_event_fn handler);
@@ -74,6 +74,7 @@ int photo_send(const char* channel_id, const char* src_file_path, const char* db
struct chat_msg_submit* req = u_calloc(1, sizeof(struct chat_msg_submit) + datalen + 1);
if (!req) return -1;
req->inst = instance_lite_get_instance();
snprintf(req->channel_id, sizeof(req->channel_id), "%s", channel_id);
snprintf(req->content_type, sizeof(req->content_type), "%s", content_type);
snprintf(req->media_src, sizeof(req->media_src), "%s", src_file_path);
+7 -3
View File
@@ -38,6 +38,7 @@ struct standby_wait_entry {
};
static struct UASYNC* g_ua = NULL;
static struct UTUN_INSTANCE* g_inst = NULL; /* для чтения chat_setting (per-instance) */
static int g_enabled = 0;
static int g_phase = PHASE_ACTIVE;
static uint64_t g_phase_start_tb = 0; /* начало текущей фазы */
@@ -59,15 +60,15 @@ static uint32_t g_cnt_net_events = 0;
static uint32_t g_cnt_net_ignored = 0;
static int standby_active_sec(void) {
return chat_setting_get_int("standby_active_sec", 2);
return chat_setting_get_int(g_inst, "standby_active_sec", 2);
}
static int standby_sleep_sec(void) {
return chat_setting_get_int("standby_sleep_sec", 60);
return chat_setting_get_int(g_inst, "standby_sleep_sec", 60);
}
static int standby_min_sleep_sec(void) {
int m = chat_setting_get_int("standby_min_sleep_sec", 15);
int m = chat_setting_get_int(g_inst, "standby_min_sleep_sec", 15);
if (m < 0) m = 0;
int s = standby_sleep_sec();
if (m > s) m = s;
@@ -195,6 +196,7 @@ static void standby_wait_timer_cb(void* arg) {
void standby_init(struct UASYNC* ua) {
g_ua = ua;
g_inst = NULL;
g_enabled = 0;
g_phase = PHASE_ACTIVE;
g_phase_timer = NULL;
@@ -225,6 +227,7 @@ void standby_deinit(void) {
}
g_enabled = 0;
g_ua = NULL;
g_inst = NULL;
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "standby: deinit (wakes_timer=%u wakes_early=%u sleeps=%u net=%u ignored=%u)",
g_cnt_wakes_timer, g_cnt_wakes_early, g_cnt_sleeps, g_cnt_net_events, g_cnt_net_ignored);
}
@@ -234,6 +237,7 @@ void standby_set_enabled(struct UTUN_INSTANCE* inst, int enabled) {
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "standby: set_enabled(%d) but not initialized", enabled);
return;
}
if (inst) g_inst = inst;
if (g_enabled == enabled) return;
if (enabled) {
@@ -63,6 +63,7 @@ int video_send(const char* channel_id, const char* src_file_path, const char* db
struct chat_msg_submit* req = u_calloc(1, sizeof(struct chat_msg_submit) + datalen + 1);
if (!req) return -1;
req->inst = instance_lite_get_instance();
snprintf(req->channel_id, sizeof(req->channel_id), "%s", channel_id);
snprintf(req->content_type, sizeof(req->content_type), "video/mp4");
snprintf(req->media_src, sizeof(req->media_src), "%s", src_file_path);
@@ -383,6 +383,7 @@ int voice_recorder_stop(int* out_duration_ms) {
struct chat_msg_submit* req = u_calloc(1, sizeof(struct chat_msg_submit) + total_data_len + 1);
if (!req) { voice_cleanup_locked(g_rec); pthread_mutex_unlock(&g_rec->mtx); pthread_mutex_unlock(&g_init_mtx); return -1; }
req->inst = instance_lite_get_instance();
snprintf(req->channel_id, sizeof(req->channel_id), "%s", g_rec->channel_id);
snprintf(req->content_type, sizeof(req->content_type), "audio/opus");
snprintf(req->media_src, sizeof(req->media_src), "%s", temp_path);