Files
utun2/src/utun_instance_doc.md
T

15 KiB
Raw Blame History

utun_instance — ядро одного узла и независимые сервисы

Полностью сверены utun_instance.c/.h и этот документ, 26.10.2026. Связанный документ service_lifecycle.md описывает также GUI, Android и media workers; аудит instance не означает завершения всех его разделов.

Использование и поток

UTUN_INSTANCE принадлежит одному узлу. Несколько экземпляров могут разделять переданный UASYNC; он принадлежит вызывающему и должен жить до конца destroy. Все операции выполняются в его потоке. Stop сервиса, reload и destroy выполняются вне callbacks останавливаемых модулей. Межпоточные команды передаются в uasync; прямые конкурентные обращения к instance недопустимы.

struct UASYNC* ua = uasync_create();
if (!ua) return -1;
struct UTUN_INSTANCE* inst = utun_instance_create(ua, "utun.cfg");
if (!inst || utun_core_start(inst) < 0 || chat_service_start(inst) < 0) {
    utun_instance_destroy(inst);
    uasync_destroy(ua, 0);
    return -1;
}
while (inst->running) uasync_poll(ua, 100);
utun_instance_destroy(inst);
uasync_destroy(ua, 0);

Цикл запускает приложение; функции utun_instance_run нет. utun_instance_stop только снимает running и вызывает wakeup. Это не stop сервисов. При общем UASYNC решение о продолжении poll принимает его владелец.

Конструкторы

API Действие и владение
create(ua, path) Проверяет аргументы, запускает config updater, разбирает файл и создаёт ядро. Updater может создать/нормализовать файл, ключи и node_id
create_from_config(ua, config) Использует готовую модель без updater. При успехе забирает config; при ошибке caller продолжает владеть им
create_from_str(ua, text) Копирует ASCIIZ текст в mkstemp /tmp/utun_cfg_XXXXXX, вызывает файловый create и удаляет temp. Требует доступной ФС; текст не сохраняется

Все возвращают NULL при ошибке и освобождают частично созданные ресурсы. При create_from_config ключи должны быть заданы корректно; производный node_id проверяется и может исправляться в модели даже при последующей ошибке. log_file и уровни debug меняют logger всего процесса, включая неудачный create. Путь лога копируется в постоянный буфер, чтобы пережить освобождение config.

Create уже выводит идентичность X25519/Ed25519, создаёт индекс сетей и соединений, пулы, routing, обязательные UDP/TCP listeners, топологию/SQLite, мониторинг и automatic sockets, firewall, router/multicast, NAT detection и настроенный proxy. Это I/O с возможными ошибками bind, каталогов, БД и ресурсов; создание ядра не ограничивается выделением структуры.

При включённой топологии SQLite открывается один раз: db_path/chats.db, без пути — :memory:. Каталог подготавливается рекурсивно. Core/UTUN/chat ещё не запущены: их флаги и running равны нулю. Неуспешный обязательный listen или proxy отклоняет create. Недоступность ICMP raw socket по правам/поддержке отключает только ICMP exit с WARN.

Запуск и остановка

API Ресурсы и результат
utun_core_start db_sync, NCD/keepalive, постоянные транспорты [client], control, время, broadcast/radio и STUN. Успех задаёт core_started=running=1
utun_service_start Требует core и топологию. Создаёт UTUN-группу, DATA binding, опциональные TUN/маршруты/NAT и отдельные групповые запросы к [client]
chat_service_start Объявлен в chat_core.h. Требует core и SQLite. Поднимает CHAT-синхронизацию, stream/media/DM/member_files/call/radio-call, headless и LAN
utun_instance_init Последовательно core, затем UTUN при наличии топологии, затем чат при chatserver_enabled

Start возвращает 0 при успехе или повторном запуске, -1 при ошибке. Готовность локальных ресурсов не гарантирует UP транспорта или READY группы. Ошибка старта UTUN/чата вызывает stop этого сервиса. Core start оставляет уже подготовленные ресурсы для retry/destroy; NTP и STUN нефатальны. init не откатывает ранее запущенные сервисы при отказе последующего.

UTUN stop снимает DATA binding, TUN/NAT/маршруты, свои групповые запросы и UTUN-группу. Постоянные [client]-транспорты ядра и чат сохраняются. Chat stop прекращает LAN/headless/stream и фоновые задачи, дожидается workers, затем закрывает медиа/DM/синхронизацию/CHAT-группы и chat core. SQLite остаётся; следующий start загружает сохранённые каналы. Stop обоих сервисов идемпотентен.

Три headless bind-поля принимают IPv4 literal и десятичный порт 0..65535. Пустая строка отключает listener; 0 выбирает порт ОС, доступный через getter. Плохой bind или ошибка listener логируется и отключает только его; чат остаётся запущенным. Call/radio stop отменяет таймеры, снимает клиентов с UASYNC, освобождает кодеки и контекст. После 11-байтового wire-заголовка PCM копируется в выровненный int16_t буфер перед Opus.

Глобальный переключатель set_tun_init_enabled по умолчанию выключен; влияет на последующий UTUN start и proxy test mode при create. Дополнительно нужен config.global.tun_enabled. set_topo_group_enabled по умолчанию включён; влияет на последующие create, включая общую БД. Это общие настройки процесса, а не операции включения/выключения ресурсов существующего instance.

Reload

Всегда сохранять возвращённый указатель:

inst = utun_instance_reload(inst, ua, "utun.cfg");
if (!inst) { /* прежнее ядро уже уничтожено; обработать ошибку */ }

Нужны существующий instance, его же UASYNC и непустой путь. Сначала переоткрывается лог, updater проверяет файл, затем готовятся новая модель и firewall. Ошибка подготовки возвращает прежний instance; updater и logger могли уже изменить файл/глобальные настройки, поэтому общей транзакции с ними нет.

Полный restart выбирается при изменении негорячих global-настроек, списков my_subnet/route_subnet, а также server-конфига в automatic режиме. Сюда входят идентичность, имя, db_path, TUN/NAT/proxy, control, keepalive и режим сокетов. Горячими global-полями считаются firewall, уровни debug и STUN. Списки сравниваются по содержимому; различия порядка и padding модели могут консервативно выбрать restart.

Полный путь уничтожает старое ядро, создаёт новое из уже разобранной модели и восстанавливает прежние core_started/utun_started/chat_started/running. Изменение config-флага само по себе не включает ранее остановленный сервис. Ошибки создания/восстановления после destroy возвращают NULL. In-memory SQLite теряет данные, дисковая БД сохраняется.

Частичный путь заранее готовит отдельный индекс сетей. В ручном режиме endpoints сопоставляются по имени [server:name] и всем параметрам: адрес/порт/IPv6 scope, UDP/TCP, интерфейс, mark/FIB, MTU, type, REALITY/SOCKS, IPv6 mode и only_local. Неизменённый endpoint сохраняет socket ID, generation и выбранный ОС порт при port=0. Удаление TCP закрывает listener и TCP-модель, UDP использует собственный teardown. Probe и endpoints других владельцев не удаляются только из-за отсутствия в server-конфиге.

Новые endpoints создаёт общая UDP/TCP factory. При изменении servers/clients перестраиваются постоянные [client]; неизменённый список сохраняет handles/таймеры. Копии клиентских параметров не ссылаются на прежний config. Далее заменяются config, networks и firewall, перепубликуются изменившиеся адреса и перезапускается STUN при работающем core.

Это не атомарная замена sockets: отказ нового endpoint сохраняет новый config и отсутствие endpoint для следующей попытки reload. Ошибка подготовки клиентов сохраняет их прежние самостоятельные handles. Ошибки выводятся в лог; socket_init_status=-1 означает отказ хотя бы одного endpoint, 0 — успех.

Подписки активности и сна пира

Activity и peer-sleep подписки принадлежат instance; fn/arg заимствованы. Пара fn+arg уникальна; повторный add ничего не меняет. Новые записи идут в голову, события доставляются синхронно в обратном порядке добавления. NULL instance/fn и remove отсутствующей записи — no-op; OOM add логируется.

В callback разрешено удалить себя/соседа и добавить подписку. Удалённые записи немедленно пропускаются, память удерживается до конца внешней рассылки обоих видов событий. Новая подписка не участвует в текущем обходе, но может получить вложенное событие. Вложенный peer-sleep исполняется до продолжения внешнего; новая эффективная activity прекращает остаток старой activity-рассылки. Reload/destroy внутри таких callbacks запрещён.

utun_set_client_activity нормализует requested mode, учитывает stream hold, применяет Android standby при соответствующей сборке и затем рассылает изменение. Повтор согласует режим без повторного события; ошибка standby откатывает состояние. SERVER возвращает 0 без изменения; phone_state этим API не меняется.

Уничтожение и диагностика

Destroy снимает running/core_started и запрещает повторный вход, останавливает STUN/NAT detection/padding, NTP/control/proxy, чат/UTUN и core-client handles, затем мониторинг/automatic sockets и топологию/broadcast/radio/multicast. Далее закрывает endpoints с учётом UDP/TCP, db_sync, STCP/REALITY, ETCP и pending pings, routing/media/router/firewall, сети, config, пулы и оставшиеся подписки. Списки сокетов и соединений обходятся заново после закрывающих callbacks. Deferred callbacks выкачиваются, пока UASYNC жив.

Общая SQLite закрывается последней. Caller обязан завершить заимствованные statements/backups до destroy. BUSY логируется, close_v2 откладывает физическое освобождение БД до закрытия оставшихся SQL-объектов; instance после возврата уже недействителен. Ни logger процесса, ни UASYNC destroy ядра не уничтожает.

utun_instance_diagnose_leaks печатает sys/info счётчики и простые sanity-проверки списков. Надпись POTENTIAL LEAK о живом ресурсе до cleanup не доказывает утечку; проверка произвольных повреждённых указателей не гарантируется. instance_find_conn ищет node_id в основном ETCP и старом TCP-реестре, возвращает заимствованный указатель либо NULL, включая NULL instance; готовность UP/READY не обещает.

Основные поля: name[MAX_CONN_NAME_LEN] (64), собственный config, node_id/ключи, заимствованный ua, три *_started и running, topo_groups/SQLite, conn_mgr (у UTUN-группы), etcp_sockets (UDP/TCP), stcp_servers (список), config_conn_handles, pools и networks. Системные маршруты принадлежат отдельному tun_routes, а не списку config. Подробные поля/типы описаны в заголовке.

Проверки и платформенные ограничения фиксируются в utun_instance_audit_findings.txt.