/* * chat_status.c — сбор статуса (NTP + ETCP connections) и отправка в GUI * * Вынесено из chat_core.c для уменьшения размера модуля. */ #include "chat_core_priv.h" #include "gui_bridge.h" #include "../../../src/utun_instance.h" #include "../../../src/ntp_time.h" #include "../../../lib/ll_queue.h" #include "../../../lib/platform_compat.h" #include "etcp.h" #include "etcp_connections.h" static const char* nat_type_str(uint8_t t) { switch (t) { case 0: return "UNKNOWN"; case 1: return "EIM"; case 2: return "STRICT"; case 3: return "DIRECT"; default: return "?"; } } static void get_node_name(uint64_t node_id, char* out, size_t sz) { out[0] = '\0'; if (!g_cc.db || node_id == 0) return; sqlite3_stmt* st = NULL; if (sqlite3_prepare_v2(g_cc.db, "SELECT name FROM nodes WHERE node_id=?", -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) snprintf(out, sz, "%s", n); } sqlite3_finalize(st); } static void chat_core_collect_status(void) { char buf[8192]; int off = 0; if (!g_cc.initialized || !g_cc.inst) { off = snprintf(buf, sizeof(buf), "uTun not initialized\n"); gui_bridge_post(GUI_EVT_STATUS_REFRESH, (const uint8_t*)buf, off); return; } struct NTP_TIME* ntp = &g_cc.inst->ntp; off += snprintf(buf + off, sizeof(buf) - off, "=== NTP ===\n"); off += snprintf(buf + off, sizeof(buf) - off, "Enabled: %s\n", ntp->enabled ? "yes" : "no"); off += snprintf(buf + off, sizeof(buf) - off, "Synced: %s\n", ntp->synced ? "yes" : "no"); off += snprintf(buf + off, sizeof(buf) - off, "Offset: %.1f s\n", ntp->offset_us / 1000000.0); if (ntp->server_count > 0 && ntp->servers) { off += snprintf(buf + off, sizeof(buf) - off, "Servers: %d\n", ntp->server_count); for (int i = 0; i < ntp->server_count && ntp->servers[i]; i++) off += snprintf(buf + off, sizeof(buf) - off, " [%d] %s%s\n", i, ntp->servers[i], i == ntp->server_current ? " (current)" : ""); } if (ntp->synced && ntp->last_sync_tb > 0) { uint64_t now_tb = get_time_tb(); uint64_t ago_sec = (now_tb - ntp->last_sync_tb) / 10000; off += snprintf(buf + off, sizeof(buf) - off, "Last sync: %llu sec ago\n", (unsigned long long)ago_sec); } int conn_count = 0; struct ll_entry* entry = g_cc.inst->connections->head; while (entry) { conn_count++; entry = entry->next; } off += snprintf(buf + off, sizeof(buf) - off, "=== Connections (%d) ===\n", conn_count); uint64_t my_id = g_cc.inst->node_id; entry = g_cc.inst->connections->head; while (entry) { struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data; if (!ce || !ce->conn) { entry = entry->next; continue; } uint64_t pid = ce->peer_node_id; struct ETCP_CONN* conn = ce->conn; int link_count = 0; struct ETCP_LINK* tl = conn->links; while (tl) { link_count++; tl = tl->next; } const char* init_str = conn->initialized ? "" : "(!init)"; char myhex[5], peerhex[5]; snprintf(myhex, sizeof(myhex), "%04llX", (unsigned long long)(my_id & 0xFFFF)); snprintf(peerhex, sizeof(peerhex), "%04llX", (unsigned long long)(pid & 0xFFFF)); char peername[64]; get_node_name(pid, peername, sizeof(peername)); if (peername[0]) off += snprintf(buf + off, sizeof(buf) - off, "[%s]→[%s] %s ETCP:%s%s(%dL)\n", myhex, peerhex, peername, conn->links_up ? "UP" : "DOWN", init_str, link_count); else off += snprintf(buf + off, sizeof(buf) - off, "[%s]→[%s] ETCP:%s%s(%dL)\n", myhex, peerhex, conn->links_up ? "UP" : "DOWN", init_str, link_count); int link_idx = 0; struct ETCP_LINK* link = conn->links; while (link) { char rtt_str[32]; rtt_str[0] = '\0'; if (link->rtt_last > 0) snprintf(rtt_str, sizeof(rtt_str), " / rtt=%ums", link->rtt_last / 10); off += snprintf(buf + off, sizeof(buf) - off, " LINK#%d: %s /NAT=%s%s\n", link_idx, link->link_status ? "UP" : "DOWN", nat_type_str(link->nat_type), rtt_str); link = link->next; link_idx++; } entry = entry->next; } gui_bridge_post(GUI_EVT_STATUS_REFRESH, (const uint8_t*)buf, off); } void chat_core_collect_status_trampoline(void* arg) { (void)arg; chat_core_collect_status(); }