From e1e54df4f4898761d613cb4c32d59c6f2fcd457e Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 3 Aug 2026 18:50:11 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20invite=20=D0=BD=D0=B5=20=D0=B7=D0=B0?= =?UTF-8?q?=D1=82=D0=B8=D1=80=D0=B0=D0=B5=D1=82=20=D0=B8=D0=BC=D1=8F=20?= =?UTF-8?q?=D1=83=D0=B7=D0=BB=D0=B0,=20=D0=B8=D0=BC=D1=8F=20=D1=87=D0=B8?= =?UTF-8?q?=D1=82=D0=B0=D0=B5=D1=82=D1=81=D1=8F=20=D0=B8=D0=B7=20member=5F?= =?UTF-8?q?sync=20peers.userinfo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - conn_mgr_core.c: убрана запись node_name из cm_handle_invite_info_resp - topo_node_sqlite.c: name в nodes не перезаписывается пустой строкой - android_jni_bridge.c: bridge_get_node_name читает peers_.userinfo если nodes.name пуст --- lib/u_async.c | 12 ++ lib/u_async.h | 8 + src/media_delivery/media_download.c | 48 ++++- src/routing_layer/conn_mgr_core.c | 22 ++- src/routing_layer/topo_group.c | 5 +- src/routing_layer/topo_node_sqlite.c | 2 +- src/transport_layer/node_conn_direct.c | 20 +- src/transport_layer/stcp_client.c | 5 + src/transport_layer/stcp_link.c | 3 +- src/transport_layer/stcp_server.c | 1 + src/utun_instance.c | 1 - .../jni_bridge/android_jni_bridge.c | 36 ++-- .../libutun_lite/instance_lite.c | 184 ++++++++++-------- tools/chatgui/src/accountlist.cpp | 12 +- 14 files changed, 234 insertions(+), 125 deletions(-) diff --git a/lib/u_async.c b/lib/u_async.c index 9edf9763..ecb4d095 100644 --- a/lib/u_async.c +++ b/lib/u_async.c @@ -1334,6 +1334,7 @@ struct UASYNC* uasync_create(void) { ua->immediate_queue_head = NULL; ua->immediate_queue_tail = NULL; ua->last_poll_exit_us = 0; + ua->running = 0; DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Creating SA..."); ua->sockets = socket_array_create(16); @@ -1547,8 +1548,19 @@ void uasync_print_resources(struct UASYNC* ua, const char* prefix) { // Modified function in u_async.c: uasync_destroy // Changes: Close wakeup sockets properly on Windows. +void uasync_mark_running(struct UASYNC* ua) { + if (!ua) return; + __atomic_store_n(&ua->running, 1, __ATOMIC_RELEASE); +} + +void uasync_mark_stopped(struct UASYNC* ua) { + if (!ua) return; + __atomic_store_n(&ua->running, 0, __ATOMIC_RELEASE); +} + void uasync_destroy(struct UASYNC* ua, int close_fds) { if (!ua) return; + ua->running = 0; DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_destroy: starting cleanup for ua=%p", ua); diff --git a/lib/u_async.h b/lib/u_async.h index 55155884..da9336d4 100644 --- a/lib/u_async.h +++ b/lib/u_async.h @@ -75,6 +75,7 @@ struct UASYNC { pthread_mutex_t posted_lock; #endif volatile sig_atomic_t stop; + volatile sig_atomic_t running; // 0=shutdown mode (API rejected), 1=full init done uint64_t last_poll_exit_us; }; @@ -82,9 +83,16 @@ struct UASYNC { typedef struct UASYNC uasync_t; typedef struct UASYNC UASYNC_t; +static inline int uasync_is_running(struct UASYNC* ua) { + if (!ua) return 0; + return __atomic_load_n(&ua->running, __ATOMIC_ACQUIRE); +} + // Instance API - основной API для работы с uasync struct UASYNC* uasync_create(void); void uasync_destroy(struct UASYNC* ua, int close_fds); +void uasync_mark_running(struct UASYNC* ua); +void uasync_mark_stopped(struct UASYNC* ua); // текущее время (timebase 0.1ms) uint64_t get_time_tb(void); diff --git a/src/media_delivery/media_download.c b/src/media_delivery/media_download.c index 4e13135b..0295a931 100644 --- a/src/media_delivery/media_download.c +++ b/src/media_delivery/media_download.c @@ -15,6 +15,7 @@ #include "../lib/platform_compat.h" #include #include +#include #include #include "../media_async/media_async.h" @@ -39,6 +40,19 @@ static void md_dl_free(struct media_download* dl) { /* ── send helpers ── */ +static void md_mkdir_parent(const char* filepath) { + char path[1024]; snprintf(path, sizeof(path), "%s", filepath); + char* p = path[0] == '/' ? path + 1 : path; + while ((p = strchr(p, '/'))) { + *p = '\0'; + if (utun_mkdir(path, 0755) != 0 && errno != EEXIST) { + DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: mkdir(%s) failed: %s", MDL_ID, path, strerror(errno)); + *p = '/'; return; + } + *p = '/'; p++; + } +} + static int md_dl_send(struct UTUN_INSTANCE* inst, uint64_t dst, const uint8_t* data, size_t len) { struct ll_entry* e = queue_entry_new(0); if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: queue_entry_new failed for send to 0x%016llx", MDL_ID, (unsigned long long)dst); return -1; } @@ -64,8 +78,9 @@ static int md_dl_send_block_req(struct UTUN_INSTANCE* inst, uint64_t dst, req.chunk = (uint32_t)bi; req.offset = 0; - DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: BLOCK_REQ to 0x%016llx group=%016llx block=%d", - MDL_ID, (unsigned long long)dst, (unsigned long long)dl->group_id, bi); + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: BLOCK_REQ to 0x%016llx group=%016llx bi=%d dl->num_blocks=%d block_id=%02x%02x%02x%02x...", + MDL_ID, (unsigned long long)dst, (unsigned long long)dl->group_id, bi, dl->num_blocks, + dl->block_ids[bi * 16], dl->block_ids[bi * 16 + 1], dl->block_ids[bi * 16 + 2], dl->block_ids[bi * 16 + 3]); return md_dl_send(inst, dst, (const uint8_t*)&req, sizeof(req)); } @@ -104,6 +119,8 @@ static void md_dl_send_have_block(struct UTUN_INSTANCE* inst, struct media_downl static void md_dl_conn_cb(struct CONN_MGR_HANDLE* h, uint64_t node_id, uint64_t group_id, enum conn_mgr_event event, void* arg) { struct media_download* dl = (struct media_download*)arg; (void)h; (void)group_id; + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: conn_cb event=%d node=0x%016llx dl=%p active=%d num_peers=%d num_blocks=%d", + MDL_ID, event, (unsigned long long)node_id, (void*)dl, dl->active, dl->num_peers, dl->num_blocks); if (event != CONN_EVENT_UP) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "%s: connect to 0x%016llx failed rc=%d", MDL_ID, (unsigned long long)node_id, event); @@ -113,9 +130,13 @@ static void md_dl_conn_cb(struct CONN_MGR_HANDLE* h, uint64_t node_id, uint64_t for (int pi = 0; pi < dl->num_peers; pi++) { if (dl->peers[pi].node_id == node_id) { dl->peers[pi].connected = 1; + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: matched peer[%d] num_blocks=%d", + MDL_ID, pi, dl->peers[pi].num_blocks); for (int bi = 0; bi < dl->peers[pi].num_blocks; bi++) { if (!dl->peers[pi].blocks[bi].started) { dl->peers[pi].blocks[bi].started = 1; + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: conn_cb BLOCK_REQ: peer-local bi=%d", + MDL_ID, bi); md_dl_send_block_req(dl->inst, node_id, dl, bi); } } @@ -234,8 +255,12 @@ void media_download_handle_query_resp(struct UTUN_INSTANCE* inst, } } - DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: QUERY_RESP entries=%d peers=%d", - MDL_ID, ne, dl->num_peers); + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: QUERY_RESP entries=%d peers=%d dl=%p num_blocks=%d", + MDL_ID, ne, dl->num_peers, (void*)dl, dl->num_blocks); + for (int pi = 0; pi < dl->num_peers; pi++) { + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: peer[%d] node=0x%016llx num_blocks=%d", + MDL_ID, pi, (unsigned long long)dl->peers[pi].node_id, dl->peers[pi].num_blocks); + } /* if no peers found, try querying the author directly */ if (dl->num_peers == 0 && dl->author_node_id && dl->author_node_id != inst->node_id) { @@ -259,11 +284,17 @@ void media_download_handle_query_resp(struct UTUN_INSTANCE* inst, for (int pi = 0; pi < dl->num_peers; pi++) { dl->peers[pi].connected = 1; - if (grp && grp->conn_mgr) - conn_mgr_open_invite(inst, grp->group_id, NULL, dl->peers[pi].node_id, md_dl_conn_cb, dl, NULL); - else - for (int bi = 0; bi < dl->peers[pi].num_blocks; bi++) + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: peer[%d] route: grp=%p conn_mgr=%p", + MDL_ID, pi, (void*)grp, grp ? (void*)grp->conn_mgr : NULL); + if (grp && grp->conn_mgr) { + int cm_rc = conn_mgr_open_invite(inst, grp->group_id, NULL, dl->peers[pi].node_id, md_dl_conn_cb, dl, NULL); + if (cm_rc != 0) DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: conn_mgr_open_invite failed rc=%d for 0x%016llx", MDL_ID, cm_rc, (unsigned long long)dl->peers[pi].node_id); + } else + for (int bi = 0; bi < dl->peers[pi].num_blocks; bi++) { + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: direct BLOCK_REQ: peer-local bi=%d peer_num_blocks=%d", + MDL_ID, bi, dl->peers[pi].num_blocks); md_dl_send_block_req(inst, dl->peers[pi].node_id, dl, bi); + } } } @@ -441,6 +472,7 @@ int media_download_start(struct UTUN_INSTANCE* inst, uint64_t group_id, memcpy(dl->media_id, result->media_id, 16); dl->group_id = group_id; snprintf(dl->dest_path, sizeof(dl->dest_path), "%s", dest_path); + md_mkdir_parent(dl->dest_path); snprintf(dl->media_base, sizeof(dl->media_base), "%s", media_base); dl->num_blocks = result->num_blocks; dl->file_size = result->file_size; diff --git a/src/routing_layer/conn_mgr_core.c b/src/routing_layer/conn_mgr_core.c index 28844373..5e6f0cce 100644 --- a/src/routing_layer/conn_mgr_core.c +++ b/src/routing_layer/conn_mgr_core.c @@ -58,6 +58,10 @@ static void cm_deliver_up_cb(void* arg) { * Все поля handle'а читаются до вызова коллбэка — коллбэк может закрыть * handle (conn_mgr_close), после вызова к handle не обращаемся. */ void cm_deliver_event(struct CONN_MGR_ENTRY* entry, enum conn_mgr_event event) { + if (!entry || !entry->mgr || !entry->mgr->group) { + DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "cm_deliver_event: BAD entry=%p mgr=%p", entry, entry ? (void*)entry->mgr : NULL); + return; + } uint64_t gid = entry->mgr->group->group_id; struct CONN_MGR_HANDLE* h = entry->handles; while (h) { @@ -221,7 +225,7 @@ void cm_update_nodeinfo(struct CONN_MGR_ENTRY* entry) { } void cm_cleanup_db_node(struct CONN_MGR_ENTRY* entry) { - if (!entry->db_loaded) return; + if (!entry || !entry->mgr || !entry->mgr->group || !entry->db_loaded) return; struct TOPO_GROUP* group = entry->mgr->group; struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(group, entry->node_id); if (!nq) return; @@ -243,7 +247,7 @@ void cm_cleanup_db_node(struct CONN_MGR_ENTRY* entry) { * DOWN — соединение упало, доставляем DOWN. */ void cm_ncd_callback(struct NODE_CONN_DIRECT* ncd_h, enum ncd_event ncd_ev, void* arg) { struct CONN_MGR_ENTRY* entry = (struct CONN_MGR_ENTRY*)arg; (void)ncd_h; - if (entry->state == CONN_MGR_STATE_DISCONNECTED) return; + if (!entry || !entry->mgr || entry->state == CONN_MGR_STATE_DISCONNECTED) return; switch (ncd_ev) { case NCD_EVENT_UP: if (entry->state == CONN_MGR_STATE_CONNECTED) return; @@ -255,13 +259,17 @@ void cm_ncd_callback(struct NODE_CONN_DIRECT* ncd_h, enum ncd_event ncd_ev, void cm_update_nodeinfo(entry); cm_deliver_event(entry, CONN_EVENT_UP); break; case NCD_EVENT_TIMEOUT: - DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "link: ETCP connect TIMEOUT to 0x%016llx — no response, trying fallback", - (unsigned long long)entry->node_id); + DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "link: ETCP connect TIMEOUT to 0x%016llx — entry=%p mgr=%p db_loaded=%d", + (unsigned long long)entry->node_id, entry, (void*)entry->mgr, entry->db_loaded); if (entry->db_loaded) { entry->main_connect_state = CM_TRY_FAILED; { struct NODE_CONN_DIRECT* ncd = entry->ncd_handle; entry->ncd_handle = NULL; if (ncd) node_conn_direct_force_close(ncd); } - cm_cleanup_db_node(entry); cm_deliver_event(entry, CONN_EVENT_TIMEOUT); + DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "link: TIMEOUT after force_close, calling cleanup"); + cm_cleanup_db_node(entry); + DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "link: TIMEOUT after cleanup, calling deliver"); + cm_deliver_event(entry, CONN_EVENT_TIMEOUT); + DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "link: TIMEOUT done"); return; } entry->main_connect_state = CM_TRY_FAILED; @@ -774,9 +782,7 @@ void cm_handle_invite_info_resp(struct CONN_MGR* mgr, struct ETCP_CONN* conn, co } if (inv->temp_nq) { struct TOPO_NODE* ni2 = topo_node_registry_find(inv->mgr->instance->topo_groups, inv->temp_nq->node_id); - if (ni2) { memcpy(ni2->ed25519_public_key, resp->ed25519_pubkey, SC_PUBKEY_SIZE); - if (ni2->node_name) { u_free(ni2->node_name); ni2->node_name = NULL; } - if (resp->node_name_len) ni2->node_name = u_strdup((const char*)resp->node_name); } + if (ni2) memcpy(ni2->ed25519_public_key, resp->ed25519_pubkey, SC_PUBKEY_SIZE); if (inv->mgr->instance->topo_sqlite_db) topo_node_sqlite_node_put(inv->mgr->instance->topo_sqlite_db, inv->mgr->instance->topo_groups, inv->temp_nq, (time_t)(get_time_tb()/10000)); } if (inv->overall_timer) { uasync_cancel_timeout(inv->mgr->instance->ua, inv->overall_timer); inv->overall_timer = NULL; } diff --git a/src/routing_layer/topo_group.c b/src/routing_layer/topo_group.c index a474dea8..e82e24cf 100644 --- a/src/routing_layer/topo_group.c +++ b/src/routing_layer/topo_group.c @@ -249,10 +249,9 @@ static void topo_group_destroy(struct TOPO_GROUP* group) { while ((e = queue_data_get(group->senders_list)) != NULL) queue_entry_free(e); queue_free(group->senders_list); - if (group->local_node) { topo_nodeq_free_group_fields(group->instance->topo_groups, group->local_node); u_free(group->local_node); } - + if (group->conn_mgr) { conn_mgr_destroy(group->conn_mgr); group->conn_mgr = NULL; } if (group->nodes) queue_free(group->nodes); - group->conn_mgr = NULL; /* destroyed externally in utun_instance_destroy before etcp_router_destroy */ + if (group->local_node) { topo_nodeq_free_group_fields(group->instance->topo_groups, group->local_node); u_free(group->local_node); } } struct TOPO_GROUPS* topo_groups_init(struct UTUN_INSTANCE* instance) { diff --git a/src/routing_layer/topo_node_sqlite.c b/src/routing_layer/topo_node_sqlite.c index 5dc562c2..ba31034d 100644 --- a/src/routing_layer/topo_node_sqlite.c +++ b/src/routing_layer/topo_node_sqlite.c @@ -119,7 +119,7 @@ int topo_node_sqlite_node_put(sqlite3* db, struct TOPO_GROUPS* groups, struct TO "INSERT INTO nodes(node_id, name, x25519_pubkey, ed25519_pubkey, last_seen_at)" " VALUES(?,?,?,?,?)" " ON CONFLICT(node_id) DO UPDATE SET" - " name=excluded.name, x25519_pubkey=excluded.x25519_pubkey," + " name=CASE WHEN excluded.name != '' THEN excluded.name ELSE nodes.name END, x25519_pubkey=excluded.x25519_pubkey," " ed25519_pubkey=excluded.ed25519_pubkey, last_seen_at=excluded.last_seen_at,\n" " created_at=COALESCE(nodes.created_at, excluded.created_at)", -1, &stmt, NULL) == SQLITE_OK) { diff --git a/src/transport_layer/node_conn_direct.c b/src/transport_layer/node_conn_direct.c index d8383155..a03df1c6 100644 --- a/src/transport_layer/node_conn_direct.c +++ b/src/transport_layer/node_conn_direct.c @@ -828,7 +828,7 @@ void node_conn_direct_close(struct NODE_CONN_DIRECT* h) { entry->fin_wait_timer = uasync_set_timeout(entry->ua, NCD_FIN_WAIT_TIMEOUT_TB, entry, ncd_fin_wait_timeout_cb, "ncd_fin_wait"); DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] fin_wait started node=0x%016llx", (unsigned long long)node_id); } else { - if (conn) { + if (conn && conn->state != 2) { etcp_conn_remove_cbk(conn, ncd_init_cb, entry); etcp_conn_remove_cbk(conn, ncd_up_cb, entry); etcp_conn_remove_cbk(conn, ncd_down_cb, entry); @@ -855,8 +855,10 @@ void node_conn_direct_close(struct NODE_CONN_DIRECT* h) { */ void node_conn_direct_force_close(struct NODE_CONN_DIRECT* h) { if (!h) return; + if (!h->entry) { DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] force_close: h=%p h->entry=NULL", h); u_free(h); return; } struct ncd_entry* entry = h->entry; - if (!entry) { u_free(h); return; } + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] force_close h=%p entry=%p node=0x%016llx handles=%d", + h, entry, (unsigned long long)entry->node_id, entry->handle_count); uint64_t node_id = entry->node_id; struct ETCP_CONN* conn = entry->conn; @@ -883,19 +885,29 @@ void node_conn_direct_force_close(struct NODE_CONN_DIRECT* h) { h->cb = NULL; h->cb_arg = NULL; if (entry->connect_timer) { uasync_cancel_timeout(entry->ua, entry->connect_timer); entry->connect_timer = NULL; } if (entry->fin_wait_timer) { uasync_cancel_timeout(entry->ua, entry->fin_wait_timer); entry->fin_wait_timer = NULL; } - if (conn) { - conn->fin_wait = 0; conn->fin_wait_clear_cb = NULL; conn->fin_wait_clear_arg = NULL; + if (conn && conn->state != 2 && conn->instance) { + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] force_close: cleaning conn=%p", conn); + conn->fin_wait = 0; + conn->fin_wait_clear_cb = NULL; conn->fin_wait_clear_arg = NULL; + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] force_close: removing ncd_init_cb"); etcp_conn_remove_cbk(conn, ncd_init_cb, entry); + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] force_close: removing ncd_up_cb"); etcp_conn_remove_cbk(conn, ncd_up_cb, entry); + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] force_close: removing ncd_down_cb"); etcp_conn_remove_cbk(conn, ncd_down_cb, entry); + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] force_close: scheduling deferred close"); if (conn->state != 2) uasync_call_soon(entry->ua, conn, ncd_deferred_close_conn); + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] force_close: removing from registry"); ncd_registry_remove(conn->instance, entry); } + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] force_close: freeing entry=%p", entry); u_free(entry); } h->entry = NULL; + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] force_close: freeing h=%p", h); u_free(h); + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] force_close: done"); } /* diff --git a/src/transport_layer/stcp_client.c b/src/transport_layer/stcp_client.c index b39775ea..9a489e91 100644 --- a/src/transport_layer/stcp_client.c +++ b/src/transport_layer/stcp_client.c @@ -64,7 +64,9 @@ static int decrypt_and_check(uint8_t *data, size_t len, struct sc_stream_state * static void client_do_close(struct stcp_conn *c, int err) { if (c->state == STCP_STATE_CLOSED || c->state == STCP_STATE_ERROR) return; + int prev = c->state; c->state = STCP_STATE_CLOSED; + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_client: closed is_server=%d err=%d prev_state=%d", c->is_server, err, prev); if (c->socket_id) { uasync_remove_socket_t(c->ua, c->sock); c->socket_id = NULL; } if (c->sock != SOCKET_INVALID) { socket_close_wrapper(c->sock); c->sock = SOCKET_INVALID; } if (c->recv_buf) { u_free(c->recv_buf); c->recv_buf = NULL; c->recv_buf_len = 0; c->recv_buf_cap = 0; } @@ -145,6 +147,7 @@ static void client_send_handshake(struct stcp_conn *c, const uint8_t *server_pub for (int i = 0; i < padding; i++) hs[SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_CLIENT + i] = (uint8_t)(salt[0] ^ i); c->state = STCP_STATE_HS_CLIENT_SENT; + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_client: handshake sent (%zu bytes), entering HS_CLIENT_SENT", total); client_try_send(c, hs, total); } @@ -163,6 +166,7 @@ static void process_server_response(struct stcp_conn *c) { uint8_t status = enc_hs[0]; uint16_t padding_size = (uint16_t)enc_hs[1] | ((uint16_t)enc_hs[2] << 8); c->hs_expected_len = SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_SERVER + padding_size; + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_client: server response OK status=%d padding=%u", status, padding_size); if (status != 0) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "server handshake status=%d", status); client_do_close(c, 4); return; } } @@ -235,6 +239,7 @@ static void client_connect_write_cb(socket_t sock, void *arg) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_client connect failed err=%d", err); client_do_close(c, err); return; } + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_client: TCP connected, starting handshake"); uasync_remove_socket_t(cli->ua, sock); c->socket_id = uasync_add_socket_t(cli->ua, sock, client_conn_read_cb, client_conn_write_cb, NULL, c); if (!c->socket_id) { client_do_close(c, ENOMEM); return; } diff --git a/src/transport_layer/stcp_link.c b/src/transport_layer/stcp_link.c index d17f2841..03f49881 100644 --- a/src/transport_layer/stcp_link.c +++ b/src/transport_layer/stcp_link.c @@ -104,7 +104,8 @@ static void client_ready_cb(struct stcp_conn *conn, void *arg) { link->etcp_conn.send_input_q = link->tx_queue; stcp_conn_set_tx_queue(conn, link->tx_queue); - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_link: client connected"); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_link: client connected, peer_pubkey=%02x%02x%02x%02x...", + link->peer_pubkey[0], link->peer_pubkey[1], link->peer_pubkey[2], link->peer_pubkey[3]); if (link->on_ready_cb) link->on_ready_cb(link, link->ready_arg); } diff --git a/src/transport_layer/stcp_server.c b/src/transport_layer/stcp_server.c index f645d1a2..7095885a 100644 --- a/src/transport_layer/stcp_server.c +++ b/src/transport_layer/stcp_server.c @@ -200,6 +200,7 @@ static void process_client_handshake(struct stcp_conn *c) { uint16_t padding_size = (uint16_t)enc_hs[0] | ((uint16_t)enc_hs[1] << 8); c->hs_expected_len = SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_CLIENT + padding_size; c->hs_key_processed = 1; + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_server: client handshake key processed, expecting %zu bytes padding", c->hs_expected_len - SC_PUBKEY_ENC_SIZE - STCP_HS_ENC_CLIENT); } static void finish_client_handshake(struct stcp_conn *c) { diff --git a/src/utun_instance.c b/src/utun_instance.c index 5515b464..31fcf1b3 100644 --- a/src/utun_instance.c +++ b/src/utun_instance.c @@ -442,7 +442,6 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) { } // Cleanup conn_mgr BEFORE sockets (unbinds from etcp_router, closes NCD handles while conns alive) - if (instance->conn_mgr) { conn_mgr_destroy(instance->conn_mgr); instance->conn_mgr = NULL; } // Cleanup ETCP sockets and connections FIRST (before destroying uasync) DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Cleaning up ETCP sockets and connections"); diff --git a/tools/chatgui-android/jni_bridge/android_jni_bridge.c b/tools/chatgui-android/jni_bridge/android_jni_bridge.c index 7c8039ab..a545de01 100644 --- a/tools/chatgui-android/jni_bridge/android_jni_bridge.c +++ b/tools/chatgui-android/jni_bridge/android_jni_bridge.c @@ -183,7 +183,7 @@ void utun_bridge_send_message(const char* channel_id, const char* text) { if (!channel_id || !text) return; struct UASYNC* ua = instance_lite_get_uasync(); - if (!ua) { bridge_log(BLEV_ERROR, "send: ua not ready"); return; } + if (!uasync_is_running(ua)) return; size_t datalen = strlen(text); struct chat_msg_submit* req = u_calloc(1, sizeof(struct chat_msg_submit) + datalen); @@ -236,10 +236,7 @@ void utun_bridge_connect_channel(const char* channel_id) { return; } struct UASYNC* ua = instance_lite_get_uasync(); - if (!ua) { - bridge_log(BLEV_ERROR, "connect_channel: ua not ready, ch=%s", channel_id); - return; - } + if (!uasync_is_running(ua)) return; char* cid = u_strdup(channel_id); if (!cid) return; bridge_log(BLEV_INFO, "connect_channel ch=%s", channel_id); @@ -267,15 +264,30 @@ static char* json_escape_alloc(const char* str) { return out; } -static void bridge_get_node_name(sqlite3* db, uint64_t node_id, char* out, size_t sz) { +static void bridge_get_node_name(sqlite3* db, uint64_t node_id, const char* channel_id, char* out, size_t sz) { out[0] = '\0'; if (!db || node_id == 0) return; sqlite3_stmt* st = NULL; - if (sqlite3_prepare_v2(db, "SELECT name FROM nodes WHERE node_id=?", -1, &st, NULL) != SQLITE_OK) return; + if (sqlite3_prepare_v2(db, "SELECT name FROM nodes WHERE node_id=?", -1, &st, NULL) == SQLITE_OK) { + sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id); + if (sqlite3_step(st) == SQLITE_ROW) { + const char* n = (const char*)sqlite3_column_text(st, 0); + if (n && n[0]) { snprintf(out, sz, "%s", n); sqlite3_finalize(st); return; } + } + sqlite3_finalize(st); + } + if (!channel_id || !channel_id[0]) return; + char peers_tbl[80], sql[256]; + peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl)); + snprintf(sql, sizeof(sql), "SELECT userinfo FROM \"%s\" WHERE node_id=?", peers_tbl); + if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return; sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id); if (sqlite3_step(st) == SQLITE_ROW) { - const char* n = (const char*)sqlite3_column_text(st, 0); - if (n && n[0]) snprintf(out, sz, "%s", n); + const char* ui = (const char*)sqlite3_column_text(st, 0); + if (ui && ui[0]) { + const char* ns = strstr(ui, "\"name\":\""); + if (ns) { ns += 8; const char* ne = strchr(ns, '"'); if (ne && ne > ns) { size_t nl = (size_t)(ne - ns); if (nl >= sz) nl = sz - 1; memcpy(out, ns, nl); out[nl] = '\0'; } } + } } sqlite3_finalize(st); } @@ -323,7 +335,7 @@ char* utun_bridge_get_channels_json(void) { if (ds) { ds += 5; char* de = strchr((char*)ds, '"'); if (de) { size_t tl = (size_t)(de - ds); if (tl > 120) tl = 120; memcpy(txt, ds, tl); txt[tl] = '\0'; } } } char author_name[128]; - bridge_get_node_name(db, (uint64_t)author, author_name, sizeof(author_name)); + bridge_get_node_name(db, (uint64_t)author, ch_id, author_name, sizeof(author_name)); if (!author_name[0]) snprintf(author_name, sizeof(author_name), "0x%016llx", (unsigned long long)author); snprintf(last_msg, sizeof(last_msg), "%s: %s", author_name, txt); @@ -408,7 +420,7 @@ char* utun_bridge_get_messages_json(const char* channel_id, int limit) { first = 0; char author_name[128]; - bridge_get_node_name(db, (uint64_t)node_id, author_name, sizeof(author_name)); + bridge_get_node_name(db, (uint64_t)node_id, channel_id, author_name, sizeof(author_name)); if (!author_name[0]) snprintf(author_name, sizeof(author_name), "0x%016llx", (unsigned long long)node_id); @@ -840,7 +852,7 @@ int utun_bridge_attachment_send(const char* channel_id, const char* file_path, c int utun_bridge_attachment_download(const char* channel_id, int64_t msg_id) { if (!channel_id || msg_id <= 0) { bridge_log(BLEV_ERROR, "attachment_download: invalid args"); return -1; } struct UASYNC* ua = instance_lite_get_uasync(); - if (!ua) { bridge_log(BLEV_ERROR, "attachment_download: no uasync"); return -1; } + if (!uasync_is_running(ua)) return -1; struct attachment_dl_req* req = u_calloc(1, sizeof(*req)); if (!req) return -1; snprintf(req->channel_id, sizeof(req->channel_id), "%s", channel_id); diff --git a/tools/chatgui-android/libutun_lite/instance_lite.c b/tools/chatgui-android/libutun_lite/instance_lite.c index f9766749..c3a821fa 100644 --- a/tools/chatgui-android/libutun_lite/instance_lite.c +++ b/tools/chatgui-android/libutun_lite/instance_lite.c @@ -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 #include #include #include #include +#include #include #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 ── */ diff --git a/tools/chatgui/src/accountlist.cpp b/tools/chatgui/src/accountlist.cpp index 40f77cb7..8380e037 100644 --- a/tools/chatgui/src/accountlist.cpp +++ b/tools/chatgui/src/accountlist.cpp @@ -5,6 +5,7 @@ #include "renamedialog.h" #include "../db/db_manager.h" #include +#include #include #include #include @@ -67,8 +68,6 @@ AccountList::AccountList(DbManager* db, QWidget *parent) m_listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_listView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); m_listView->setContextMenuPolicy(Qt::CustomContextMenu); - layout->addWidget(m_listView, 3); - m_detailText = new QTextEdit(this); m_detailText->setReadOnly(true); m_detailText->setFont(QFont("monospace", 10)); @@ -76,7 +75,14 @@ AccountList::AccountList(DbManager* db, QWidget *parent) "QTextEdit { border-top: 1px solid palette(mid); border-bottom: none;" " border-left: none; border-right: none; padding: 6px; background: palette(base); }"); m_detailText->setPlaceholderText("Select a member"); - layout->addWidget(m_detailText, 2); + + auto *memberSplitter = new QSplitter(Qt::Vertical, this); + memberSplitter->addWidget(m_listView); + memberSplitter->addWidget(m_detailText); + memberSplitter->setStretchFactor(0, 3); + memberSplitter->setStretchFactor(1, 2); + memberSplitter->setSizes({360, 240}); + layout->addWidget(memberSplitter, 1); connect(m_listView, &QListView::clicked, this, &AccountList::onMemberClicked); connect(m_listView, &QListView::doubleClicked, this, &AccountList::onMemberDoubleClicked);