KasperskyOS Community Edition 1.2
[Topic sc_using_new_endpoints]

Обзор: структура IPC-сообщения

В KasperskyOS все взаимодействия между процессами статически типизированы. Допустимые структуры IPC-сообщения определяются IDL-описаниями серверов.

IPC-сообщение (как запрос, так и ответ) содержит фиксированную часть и опционально арену.

Фиксированная часть IPC-сообщения

Фиксированная часть IPC-сообщения содержит RIID, MID и опционально параметры интерфейсных методов фиксированного размера.

Параметры фиксированного размера – это параметры, которые имеют IDL-типы фиксированного размера.

RIID и MID идентифицируют вызываемый интерфейс и метод:

  • RIID (Runtime Implementation ID) является порядковым номером используемой службы в наборе служб сервера (начиная с нуля).
  • MID (Method ID) является порядковым номером вызываемого метода в наборе методов используемой службы (начиная с нуля).

Тип фиксированной части IPC-сообщения генерируется компилятором NK на основе IDL-описания интерфейса. Для каждого метода интерфейса генерируется отдельная структура. Также генерируются типы union для хранения любого запроса к процессу, компоненту или интерфейсу. Подробнее см. Пример генерации транспортных методов и типов.

Арена IPC-сообщения

Арена IPC-сообщения (далее также арена) содержит параметры интерфейсных методов (и/или элементы этих параметров) переменного размера.

Параметры переменного размера – это параметры, которые имеют IDL‑типы переменного размера.

Подробнее см. "Работа с ареной IPC-сообщений".

Максимальный размер IPC-сообщения

Максимальный размер IPC-сообщения определяется параметрами ядра KasperskyOS. На большинстве поддерживаемых KasperskyOS аппаратных платформ совокупный размер фиксированной части и арены IPC-сообщения не может превышать 4, 8 или 16 МБ.

Проверка структуры IPC-сообщения модулем безопасности

Перед тем как вызывать связанные с IPC-сообщением правила, подсистема Kaspersky Security Module проверяет отправляемое IPC-сообщение на корректность. Проверяются как запросы, так и ответы. Если IPC-сообщение имеет некорректную структуру, оно будет отклонено без вызова связанных с ним методов моделей безопасности.

Реализация IPC-взаимодействия

Чтобы упростить разработчику работу над реализацией IPC-взаимодействия, в составе KasperskyOS Community Edition поставляются:

Реализация простейшего IPC-взаимодействия показана в примерах echo и ping (/opt/KasperskyOS-Community-Edition-<version>/examples/).

В начало
[Topic ipc_message_structure_overview]

Получение IPC-дескриптора

Клиентский и серверный IPC-дескрипторы требуется получить, если для используемой службы нет готовых транспортных библиотек (например, вы написали собственную службу). Для самостоятельной работы с IPC-транспортом нужно предварительно инициализировать его с помощью метода NkKosTransport_Init(), передав в качестве второго аргумента IPC-дескриптор используемого канала.

Подробнее см. примеры echo и ping (/opt/KasperskyOS-Community-Edition-<version>/examples/).

Для использования служб, которые реализованы в исполняемых файлах в составе KasperskyOS Community Edition, нет необходимости получать IPC-дескриптор. Вся работа с транспортом, включая получение IPC-дескрипторов, выполняется поставляемыми транспортными библиотеками.

См. примеры gpio_*, net_*, net2_* и multi_vfs_* (/opt/KasperskyOS-Community-Edition-<version>/examples/).

Получение IPC-дескриптора при статическом создании канала

При статическом создании IPC-канала как клиент, так и сервер могут сразу после своего запуска получить свои IPC-дескрипторы с помощью методов ServiceLocatorRegister() и ServiceLocatorConnect(), указав имя созданного IPC-канала.

Например, если IPC-канал имеет имя server_connection, то на клиентской стороне необходимо вызвать:

#include <coresrv/sl/sl_api.h> … Handle handle = ServiceLocatorConnect("server_connection");

На серверной стороне необходимо вызвать:

#include <coresrv/sl/sl_api.h> … ServiceId id; Handle handle = ServiceLocatorRegister("server_connection", NULL, 0, &id);

Подробнее см. примеры echo и ping (/opt/KasperskyOS-Community-Edition-<version>/examples/), а также заголовочный файл /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/sl/sl_api.h.

Закрытие полученного IPC-дескриптора приведет к недоступности IPC-канала. Если IPC-дескриптор был закрыт, то получить его повторно и восстановить доступ к IPC-каналу невозможно.

Получение IPC-дескриптора при динамическом создании канала

Как клиент, так и сервер получают свои IPC-дескрипторы сразу при успешном динамическом создании IPC-канала.

Клиентский IPC-дескриптор является одним из выходных (out) аргументов метода KnCmConnect(). Серверный IPC-дескриптор является выходным аргументом метода KnCmAccept(). Подробнее см. заголовочный файл /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/cm/cm_api.h.

Если динамически созданный IPC-канал больше не требуется, его клиентский и серверный дескрипторы нужно закрыть. При необходимости IPC-канал может быть создан снова.

В начало
[Topic ipc_find_ipc_desc]

Получение идентификатора службы (riid)

Идентификатор службы (riid) требуется получить на клиентской стороне, если для используемой службы нет готовых транспортных библиотек (например, вы написали собственную службу). Для вызова методов сервера необходимо на клиентской стороне предварительно вызвать метод инициализации прокси-объекта, передав в качестве третьего параметра идентификатор службы. Например, для интерфейса Filesystem:

Filesystem_proxy_init(&proxy, &transport.base, riid);

Подробнее см. примеры echo и ping (/opt/KasperskyOS-Community-Edition-<version>/examples/).

Для использования служб, которые реализованы в исполняемых файлах в составе KasperskyOS Community Edition, нет необходимости получать идентификатор службы. Вся работа с транспортом выполняется поставляемыми транспортными библиотеками.

См. примеры gpio_*, net_*, net2_* и multi_vfs_* (/opt/KasperskyOS-Community-Edition-<version>/examples/).

Получение идентификатора службы при статическом создании канала

При статическом создании IPC-канала клиент может получить идентификатор нужной службы с помощью метода ServiceLocatorGetRiid(), указав дескриптор IPC-канала и квалифицированное имя службы. Например, если экземпляр компонента OpsComp предоставляет службу FS, то на клиентской стороне необходимо вызвать:

#include <coresrv/sl/sl_api.h>nk_iid_t riid = ServiceLocatorGetRiid(handle, "OpsComp.FS");

Подробнее см. примеры echo и ping (/opt/KasperskyOS-Community-Edition-<version>/examples/), а также заголовочный файл /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/sl/sl_api.h.

Получение идентификатора службы при динамическом создании канала

Клиент получает идентификатор службы сразу при успешном динамическом создании IPC-канала. Клиентский IPC-дескриптор является одним из выходных (out) аргументов метода KnCmConnect(). Подробнее см. заголовочный файл /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/cm/cm_api.h.

В начало
[Topic ipc_find_riid]

Пример генерации транспортных методов и типов

При сборке решения компилятор NK на основе EDL-, CDL- и IDL-описаний генерирует набор специальных методов и типов, упрощающих формирование, отправку, прием и обработку IPC-сообщений.

В качестве примера рассмотрим класс процессов Server, предоставляющий службу FS, которая содержит единственный метод Open():

Server.edl

entity Server /* OpsComp - именованный экземпляр компонента Operations */ components { OpsComp: Operations }

Operations.cdl

component Operations /* FS - локальное имя службы, реализующей интерфейс Filesystem */ endpoints { FS: Filesystem }

Filesystem.idl

package Filesystem interface { Open(in string<256> name, out UInt32 h); }

На основе этих описаний будут сгенерированы файлы Server.edl.h, Operations.cdl.h, и Filesystem.idl.h содержащие следующие методы и типы:

Методы и типы, общие для клиента и сервера

  • Абстрактные интерфейсы, содержащие указатели на реализации входящих в них методов.

    В нашем примере будет сгенерирован один абстрактный интерфейс – Filesystem:

    typedef struct Filesystem { const struct Filesystem_ops *ops; } Filesystem; typedef nk_err_t Filesystem_Open_fn(struct Filesystem *, const struct Filesystem_Open_req *, const struct nk_arena *, struct Filesystem_Open_res *, struct nk_arena *); typedef struct Filesystem_ops { Filesystem_Open_fn *Open; } Filesystem_ops;
  • Набор интерфейсных методов.

    При вызове интерфейсного метода в запросе автоматически проставляются соответствующие значения RIID и MID.

    В нашем примере будет сгенерирован единственный интерфейсный метод Filesystem_Open:

    nk_err_t Filesystem_Open(struct Filesystem *self, struct Filesystem_Open_req *req, const struct nk_arena *req_arena, struct Filesystem_Open_res *res, struct nk_arena *res_arena)

Методы и типы, используемые только на клиенте

  • Типы прокси-объектов.

    Прокси-объект используется как аргумент интерфейсного метода. В нашем примере будет сгенерирован единственный тип прокси-объекта Filesystem_proxy:

    typedef struct Filesystem_proxy { struct Filesystem base; struct nk_transport *transport; nk_iid_t iid; } Filesystem_proxy;
  • Функции для инициализации прокси-объектов.

    В нашем примере будет сгенерирована единственная инициализирующая функция Filesystem_proxy_init:

    void Filesystem_proxy_init(struct Filesystem_proxy *self, struct nk_transport *transport, nk_iid_t iid)
  • Типы, определяющие структуру фиксированной части сообщения для каждого конкретного метода.

    В нашем примере будет сгенерировано два таких типа: Filesystem_Open_req (для запроса) и Filesystem_Open_res (для ответа).

    typedef struct __nk_packed Filesystem_Open_req { __nk_alignas(8) struct nk_message base_; __nk_alignas(4) nk_ptr_t name; } Filesystem_Open_req; typedef struct Filesystem_Open_res { union { struct { __nk_alignas(8) struct nk_message base_; __nk_alignas(4) nk_uint32_t h; }; struct { __nk_alignas(8) struct nk_message base_; __nk_alignas(4) nk_uint32_t h; } res_; struct Filesystem_Open_err err_; }; } Filesystem_Open_res;

Методы и типы, используемые только на сервере

  • Тип, содержащий все службы компонента, а также инициализирующая функция. (Для каждого компонента сервера.)

    При наличии вложенных компонентов этот тип также содержит их экземпляры, а инициализирующая функция принимает соответствующие им инициализированные структуры. Таким образом, при наличии вложенных компонентов, их инициализацию необходимо начинать с самого вложенного.

    В нашем примере будет сгенерирована структура Operations_component и функция Operations_component_init:

    typedef struct Operations_component { struct Filesystem *FS; }; void Operations_component_init(struct Operations_component *self, struct Filesystem *FS)
  • Тип, содержащий все службы, предоставляемые сервером непосредственно; все экземпляры компонентов, входящие в сервер; а также инициализирующая функция.

    В нашем примере будет сгенерирована структура Server_entity и функция Server_entity_init:

    #define Server_entity Server_component typedef struct Server_component { struct : Operations_component *OpsComp; } Server_component; void Server_entity_init(struct Server_entity *self, struct Operations_component *OpsComp)
  • Типы, определяющие структуру фиксированной части сообщения для любого метода конкретного интерфейса.

    В нашем примере будет сгенерировано два таких типа: Filesystem_req (для запроса) и Filesystem_res (для ответа).

    typedef union Filesystem_req { struct nk_message base_; struct Filesystem_Open_req Open; }; typedef union Filesystem_res { struct nk_message base_; struct Filesystem_Open_res Open; };
  • Типы, определяющие структуру фиксированной части сообщения для любого метода любой службы конкретного компонента.

    При наличии вложенных компонентов эти типы также содержат структуры фиксированной части сообщения для любых методов любых служб, включенных во все вложенные компоненты.

    В нашем примере будет сгенерировано два таких типа: Operations_component_req (для запроса) и Operations_component_res (для ответа).

    typedef union Operations_component_req { struct nk_message base_; Filesystem_req FS; } Operations_component_req; typedef union Operations_component_res { struct nk_message base_; Filesystem_res FS; } Operations_component_res;
  • Типы, определяющие структуру фиксированной части сообщения для любого метода любой службы конкретного компонента, экземпляр которого входит в сервер.

    При наличии вложенных компонентов эти типы также содержат структуры фиксированной части сообщения для любых методов любых служб, включенных во все вложенные компоненты.

    В нашем примере будет сгенерировано два таких типа: Server_entity_req (для запроса) и Server_entity_res (для ответа).

    #define Server_entity_req Server_component_req typedef union Server_component_req { struct nk_message base_; Filesystem_req OpsComp_FS; } Server_component_req; #define Server_entity_res Server_component_res typedef union Server_component_res { struct nk_message base_; Filesystem_res OpsComp_FS; } Server_component_res;
  • Dispatch-методы (диспетчеры) для отдельного интерфейса, компонента или класса процессов.

    Диспетчеры анализируют полученный запрос (значения RIID и MID), вызывают реализацию соответствующего метода, после чего сохраняют ответ в буфер. В нашем примере будут сгенерированы диспетчеры Filesystem_interface_dispatch, Operations_component_dispatch и Server_entity_dispatch.

    Диспетчер класса процессов обрабатывает запрос и вызывает методы, реализуемые этим классом. Если запрос содержит некорректный RIID (например, относящийся к другой службе, которой нет у этого класса процессов) или некорректный MID, диспетчер возвращает NK_EOK или NK_ENOENT.

    nk_err_t Server_entity_dispatch(struct Server_entity *self, const struct nk_message *req, const struct nk_arena *req_arena, struct nk_message *res, struct nk_arena *res_arena)

    В специальных случаях можно использовать диспетчеры интерфейса и компонента. Они принимают дополнительный аргумент – ID реализации интерфейса (nk_iid_t). Запрос будет обработан только если переданный аргумент и RIID из запроса совпадают, а MID корректен. В противном случае диспетчеры возвращают NK_EOK или NK_ENOENT.

    nk_err_t Operations_component_dispatch(struct Operations_component *self, nk_iid_t iidOffset, const struct nk_message *req, const struct nk_arena *req_arena, struct nk_message *res, struct nk_arena *res_arena) nk_err_t Filesystem_interface_dispatch(struct Filesystem *impl, nk_iid_t iid, const struct nk_message *req, const struct nk_arena *req_arena, struct nk_message *res, struct nk_arena *res_arena)
В начало
[Topic transport_code_overview]

Работа с ареной IPC-сообщений

Общие сведения об арене

С точки зрения разработчика решения на базе KasperskyOS арена IPC-сообщений представляет собой байтовый буфер в памяти процесса, предназначенный для хранения передаваемых через IPC данных переменного размера, то есть входных, выходных и error-параметров интерфейсных методов (и/или элементов этих параметров), которые имеют IDL-типы переменного размера. Также арена используется при обращении к модулю безопасности Kaspersky Security Module для хранения входных параметров методов интерфейса безопасности (и/или элементов этих параметров), которые имеют IDL-типы переменного размера. (Параметры интерфейсных методов постоянного размера хранятся в фиксированной части IPC-сообщения.) Арены используются как на стороне клиента, так и на стороне сервера. Одна арена предназначена либо для передачи, либо для приема через IPC данных переменного размера, но не для передачи и приема этих данных одновременно, то есть условно арены можно разделить на арены IPC-запросов (содержат входные параметры интерфейсных методов) и арены IPC-ответов (содержат выходные и error-параметры интерфейсных методов).

Через IPC передается только использованная часть арены, то есть занятая данными. (При отсутствии данных арена не передается.) Использованная часть арены включает один или несколько участков. В одном участке арены хранится массив объектов одного типа, например, массив однобайтовых объектов или массив структур. В разных участках арены могут храниться массивы объектов разного типа. Адрес начала арены должен быть выравнен на границу 2^N-байтовой последовательности, где 2^N – значение, которое больше либо равно размеру наибольшего примитивного типа в арене (например, наибольшего поля примитивного типа в структуре). Адрес участка арены также должен быть выравнен на границу 2^N-байтовой последовательности, где 2^N – значение, которое больше либо равно размеру наибольшего примитивного типа в участке арены.

Необходимость наличия нескольких участков в арене возникает, если интерфейсный метод имеет несколько входных, выходных или error-параметров переменного размера, а также если несколько элементов входных, выходных или error-параметров интерфейсного метода имеют переменный размер. Например, если интерфейсный метод имеет входной параметр IDL-типа sequence и входной параметр IDL-типа bytes, то в арене IPC-запросов будет как минимум два участка, но могут потребоваться и дополнительные участки, если параметр IDL-типа sequence состоит из элементов IDL-типа переменного размера (например, string, то есть элементами последовательности являются строковые буферы). Также, к примеру, если интерфейсный метод имеет один выходной параметр IDL-типа struct, который содержит два поля типа bytes и string, то в арене IPC-ответов будет два участка.

Из-за выравнивания адресов участков арены между этими участками могут появляться неиспользованные промежутки, поэтому размер использованной части арены может превышать размер помещенных в нее данных.

API для работы с ареной

Набор функций и макросов для работы с ареной определен в заголовочном файле sysroot-*-kos/include/nk/arena.h из состава KasperskyOS SDK. Также функция для копирования строки в арену объявлена в заголовочном файле sysroot-*-kos/include/coresrv/nk/transport-kos.h из состава KasperskyOS SDK.

Сведения о функциях и макросах, определенных в заголовочном файле sysroot-*-kos/include/nk/arena.h, приведены в таблице ниже. В этих функциях и макросах арена и участок арены идентифицируются дескриптором арены (тип nk_arena) и дескриптором участка арены (тип nk_ptr_t) соответственно. Дескриптор арены представляет собой структуру, содержащую три указателя: на начало арены, на начало неиспользованной части арены и на конец арены. Дескриптор участка арены представляет собой структуру, содержащую смещение участка арены в байтах (относительно начала арены) и размер участка арены в байтах. (Тип дескриптора участка арены определен в заголовочном файле sysroot-*-kos/include/nk/types.h. из состава KasperskyOS SDK.)

Создание арены

Чтобы передавать через IPC параметры интерфейсных методов переменного размера, нужно создать арены как на стороне клиента, так и на стороне сервера. (При обработке IPC-запросов на стороне сервера с использованием функции NkKosDoDispatch(), определенной в заголовочном файле sysroot-*-kos/include/coresrv/nk/transport-kos-dispatch.h из состава KasperskyOS SDK, арены IPC-запросов и IPC-ответов создаются автоматически.)

Чтобы создать арену, нужно создать буфер (в стеке или куче) и инициализировать дескриптор арены.

Адрес буфера должен быть выравнен так, чтобы удовлетворять максимальному размеру примитивного типа, который может быть помещен в этот буфер. Адрес буфера, созданного динамически, обычно имеет достаточное выравнивание для помещения в этот буфер данных примитивного типа максимального размера. Чтобы обеспечить требуемое выравнивание адреса статически созданного буфера, можно использовать спецификатор alignas.

Чтобы инициализировать дескриптор арены, используя указатель на уже созданный буфер, нужно использовать функцию или макрос API:

  • макрос NK_ARENA_INITIALIZER();
  • функцию nk_arena_init();
  • функцию nk_arena_create();
  • макрос NK_ARENA_FINAL();
  • макрос nk_arena_init_final().

Тип указателя не имеет значения, поскольку в коде функций и макросов API этот указатель приводится к указателю на однобайтовый объект.

Макрос NK_ARENA_INITIALIZER() и функции nk_arena_init() и nk_arena_create() инициализируют дескриптор арены, которая может содержать один и более участков. Макросы NK_ARENA_FINAL() и nk_arena_init_final() инициализируют дескриптор арены, которая на протяжении всего своего жизненного цикла содержит только один участок, занимающий всю арену.

Чтобы создать буфер в стеке и инициализировать дескриптор одним шагом, нужно использовать макрос NK_ARENA_AUTO(). Этот макрос создает арену, которая может содержать один и более участков, а адрес буфера, созданного этим макросом, имеет достаточное выравнивание для помещения в этот буфер данных примитивного типа максимального размера.

Размер арены должен быть достаточен, чтобы с учетом выравнивания адресов участков вместить параметры переменного размера для IPC-запросов или IPC-ответов одного интерфейсного метода или множества интерфейсных методов, соответствующих одному интерфейсу, компоненту или классу процессов. Автоматически генерируемый транспортный код (заголовочные файлы *.idl.h, *.cdl.h, *.edl.h) содержит константы *_arena_size, значения которых гарантированно соответствуют достаточным размерам арен в байтах.

Заголовочные файлы *.idl.h, *.cdl.h, *.edl.h содержат следующие константы *_arena_size:

  • <имя интерфейса>_<имя интерфейсного метода>_req_arena_size – размер арены IPC-запросов для указанного интерфейсного метода указанного интерфейса;
  • <имя интерфейса>_<имя интерфейсного метода>_res_arena_size – размер арены IPC-ответов для указанного интерфейсного метода указанного интерфейса;
  • <имя интерфейса>_req_arena_size – размер арены IPC-запросов для любого интерфейсного метода указанного интерфейса;
  • <имя интерфейса>_res_arena_size – размер арены IPC-ответов для любого интерфейсного метода указанного интерфейса.

Заголовочные файлы *.cdl.h, *.edl.h дополнительно содержат следующие константы *_arena_size:

  • <имя компонента>_component_req_arena_size – размер арены IPC-запросов для любого интерфейсного метода указанного компонента;
  • <имя компонента>_component_res_arena_size – размер арены IPC-ответов для любого интерфейсного метода указанного компонента.

Заголовочные файлы *.edl.h дополнительно содержат следующие константы *_arena_size:

  • <имя класса процессов>_entity_req_arena_size – размер арены IPC-запросов для любого интерфейсного метода указанного класса процессов;
  • <имя класса процессов>_entity_res_arena_size – размер арены IPC-ответов для любого интерфейсного метода указанного класса процессов.

Константы, содержащие размер арены IPC-запросов или IPC-ответов для одного интерфейсного метода (<имя интерфейса>_<имя интерфейсного метода>_req_arena_size и <имя интерфейса>_<имя интерфейсного метода>_res_arena_size), предназначены для использования на стороне клиента. Остальные константы могут использоваться как на стороне клиента, так и на стороне сервера.

Примеры создания арены:

/* Пример 1 */ alignas(8) char reqBuffer[Write_WriteInLog_req_arena_size]; struct nk_arena reqArena = NK_ARENA_INITIALIZER( reqBuffer, reqBuffer + sizeof(reqBuffer)); /* Пример 2 */ struct nk_arena res_arena; char res_buf[kl_rump_DhcpcdConfig_GetOptionNtpServers_res_arena_size]; nk_arena_init(&res_arena, res_buf, res_buf + sizeof(res_buf)); /* Пример 3 */ char req_buffer[kl_CliApplication_Run_req_arena_size]; struct nk_arena req_arena = nk_arena_create(req_buffer, sizeof(req_buffer)); /* Пример 4 */ nk_ptr_t ptr; const char *cstr = "example"; nk_arena arena = NK_ARENA_FINAL(&ptr, cstr, strlen(cstr)); /* Пример 5 */ const char *path = "path_to_file"; size_t len = strlen(path); /* Структура для сохранения фиксированной части IPC-запроса */ struct kl_VfsFilesystem_Rmdir_req req; struct nk_arena req_arena; nk_arena_init_final(&req_arena, &req.path, path, len); /* Пример 6 */ struct nk_arena res_arena = NK_ARENA_AUTO(kl_Klog_component_res_arena_size);

Заполнение арены данными перед передачей через IPC

Перед передачей IPC-запроса на стороне клиента или IPC-ответа на стороне сервера арену нужно заполнить данными. Если для создания арены используется макрос NK_ARENA_FINAL() или nk_arena_init_final(), то резервировать участок арены не требуется, а нужно только заполнить этот участок данными. Если для создания арены используется макрос NK_ARENA_INITIALIZER() или NK_ARENA_AUTO() либо функция nk_arena_init() или nk_arena_create(), то в арене необходимо зарезервировать один или несколько участков, чтобы поместить в них данные. Чтобы зарезервировать участок арены, нужно использовать функцию или макрос API:

  • функцию __nk_arena_alloc();
  • макрос nk_arena_store();
  • функцию __nk_arena_store();
  • макрос nk_arena_alloc();
  • функцию NkKosCopyStringToArena().

Дескриптор участка арены, который передается через выходной параметр этих функций и макросов, а также макросов NK_ARENA_FINAL() и nk_arena_init_final(), нужно поместить в фиксированную часть либо в арену IPC-сообщения. Если интерфейсный метод имеет параметр переменного размера, то фиксированная часть IPC-сообщений вместо самого параметра содержит дескриптор участка арены, в котором находится этот параметр. Если интерфейсный метод имеет параметр постоянного размера с элементами переменного размера, то фиксированная часть IPC-сообщений вместо самих элементов параметра содержит дескрипторы участков арены, в которых находятся эти элементы параметра. Если интерфейсный метод имеет параметр переменного размера, содержащий элементы переменного размера, то фиксированная часть IPC-сообщений содержит дескриптор участка арены, в котором находятся дескрипторы других участков арены, содержащих эти элементы параметра.

Макрос nk_arena_store() и функции __nk_arena_store() и NkKosCopyStringToArena() не только резервируют участок арены, но и копируют данные в этот участок.

Макрос nk_arena_alloc() позволяет получить адрес зарезервированного участка арены. Также адрес участка арены можно получить, используя функцию __nk_arena_get() или макрос nk_arena_get(), которые дополнительно через выходной параметр передают размер арены.

Зарезервированный участок арены можно уменьшить. Для этого нужно использовать макрос nk_arena_shrink() или функцию _nk_arena_shrink().

Чтобы отменить текущее резервирование участков арены для последующего резервирования новых участков под другие данные (после отправки IPC-сообщения), нужно вызвать функцию nk_arena_reset(). Если для создания арены используется макрос NK_ARENA_FINAL() или nk_arena_init_final(), то отменять резервирование участка не требуется, так как такая арена на протяжении всего своего жизненного цикла содержит один участок, занимающий всю арену.

Примеры заполнения арены данными:

/* Пример 1 */ char req_buffer[kl_rump_NpfctlFilter_TableAdd_req_arena_size]; struct nk_arena req_arena = NK_ARENA_INITIALIZER(req_buffer, req_buffer + sizeof(req_buffer)); /* Структура для сохранения фиксированной части IPC-запроса */ struct kl_rump_NpfctlFilter_TableAdd_req req; if (nk_arena_store(char, &req_arena, &req.tid, tid, tidlen)) return ENOMEM; if (nk_arena_store(char, &req_arena, &req.cidrAddr, cidr_addr, cidr_addrlen)) return ENOMEM; /* Пример 2 */ char req_arena_buf[StringMaxSize]; struct nk_arena req_arena = NK_ARENA_INITIALIZER(req_arena_buf, req_arena_buf + sizeof(req_arena_buf)); /* Структура для сохранения фиксированной части IPC-запроса */ kl_drivers_FBConsole_SetFont_req req; size_t buf_size = strlen(fileName) + 1; char *buf = nk_arena_alloc(char, &req_arena, &req.fileName, buf_size); memcpy(buf, fileName, buf_size); /* Пример 3 */ char reqArenaBuf[kl_core_DCM_req_arena_size]; struct nk_arena reqArena = NK_ARENA_INITIALIZER(reqArenaBuf, reqArenaBuf + sizeof(reqArenaBuf)); /* Структура для сохранения фиксированной части IPC-запроса */ kl_core_DCM_Subscribe_req req; rc = NkKosCopyStringToArena(&reqArena, &req.endpointType, endpointType); if (rc != rcOk) return rc; rc = NkKosCopyStringToArena(&reqArena, &req.endpointName, endpointName); if (rc != rcOk) return rc; rc = NkKosCopyStringToArena(&reqArena, &req.serverName, serverName); if (rc != rcOk) return rc; /* Пример 4 */ unsigned counter = 0; nk_ptr_t *paths; /* Резервирование участка арены для дескрипторов других участков арены */ paths = nk_arena_alloc(nk_ptr_t, resArena, &res->logRes, msgCount); while(...) { ... /* Резервирование участков арены с сохранением их дескрипторов в * ранее зарезервированном участке арены с адресом paths */ char *str = nk_arena_alloc( char, resArena, &paths[counter], stringLength + 1); if (str == NK_NULL) return NK_ENOMEM; snprintf(str, (stringLength + 1), "%s", buffer); ... counter++; }

Получение данных из арены после приема через IPC

Перед получением IPC-запроса на стороне сервера или IPC-ответа на стороне клиента для арены, в которую будут помещены полученные через IPC-данные, нужно отменить текущее резервирование участков, вызвав функцию nk_arena_reset(). Это требуется сделать, даже если для создания арены используется макрос NK_ARENA_FINAL() или nk_arena_init_final(). (Макросы NK_ARENA_INITIALIZER() и NK_ARENA_AUTO(), а также функции nk_arena_init() и nk_arena_create() создают арену без зарезервированных участков. Перед однократном использовании такой арены для сохранения полученных через IPC данных вызывать функцию nk_arena_reset() не требуется.)

Чтобы получить указатели на участки арены и размеры этих участков, нужно использовать функцию __nk_arena_get() или макрос nk_arena_get(), передавая через входной параметр соответствующие дескрипторы участков арены, полученные из фиксированной части и арены IPC-сообщения.

Пример получения данных из арены:

struct nk_arena res_arena; char res_buf[kl_rump_DhcpcdConfig_Version_res_ver_size]; nk_arena_init(&res_arena, res_buf, res_buf + sizeof(res_buf)); /* Структура для сохранения IPC-запроса */ struct kl_rump_DhcpcdConfig_Version_req req; req.buflen = buflen; /* Структура для сохранения IPC-ответа */ struct kl_rump_DhcpcdConfig_Version_res res; /* Вызов интерфейсного метода */ if (kl_rump_DhcpcdConfig_Version(dhcpcd.proxy, &req, NULL, &res, &res_arena) != NK_EOK) return -1; size_t ptrlen; char *ptr = nk_arena_get(char, &res_arena, &res.ver, &ptrlen); memcpy(buf, ptr, ptrlen);

Дополнительные возможности API

Чтобы получить размер арены, нужно вызвать функцию nk_arena_capacity().

Чтобы получить размер использованной части арены, нужно вызвать функцию nk_arena_allocated_size().

Чтобы проверить, является ли корректным дескриптор участка арены, нужно использовать макрос nk_arena_validate() или функцию __nk_arena_validate().

Сведения о функциях и макросах API

Функции и макросы arena.h

Функция/Макрос

Сведения о функции/макросе

NK_ARENA_INITIALIZER()

Назначение

Инициализирует дескриптор арены.

Параметры

  • [in] _start – указатель на начало арены.
  • [in] _end – указатель на конец арены.

Значения макроса

Код инициализации дескриптора арены.

nk_arena_init()

Назначение

Инициализирует дескриптор арены.

Параметры

  • [out] self – указатель на дескриптор арены.
  • [in] start – указатель на начало арены.
  • [in] end – указатель на конец арены.

Возвращаемые значения

Нет.

nk_arena_create()

Назначение

Создает и инициализирует дескриптор арены.

Параметры

  • [in] start – указатель на начало арены.
  • [in] size – размер арены в байтах.

Возвращаемые значения

Дескриптор арены.

NK_ARENA_AUTO()

Назначение

Создает в стеке буфер, а также создает и инициализирует дескриптор арены.

Параметры

  • [in] size – размер арены в байтах. Должен быть задан константой.

Значения макроса

Дескриптор арены.

NK_ARENA_FINAL()

Назначение

Инициализирует дескриптор арены, содержащей только один участок.

Параметры

  • [out] ptr – указатель на дескриптор участка арены.
  • [in] start – указатель на начало арены.
  • [in] count – число объектов в участке арены.

Значения макроса

Дескриптор арены.

nk_arena_reset()

Назначение

Отменяет резервирование участков арены.

Параметры

  • [in,out] self – указатель на дескриптор арены.

Возвращаемые значения

Нет.

__nk_arena_alloc()

Назначение

Резервирует участок арены заданного размера с заданным выравниванием.

Параметры

  • [in,out] self – указатель на дескриптор арены.
  • [out] ptr – указатель на дескриптор участка арены.
  • [in] size – размер участка арены в байтах.
  • [in] align – значение, задающее выравнивание участка арены. Адрес участка арены может быть невыравненным (align=1) или выравненным (align=2,4,...,2^N) на границу 2^N-байтовой последовательности (например, двухбайтовой, четырехбайтовой).

Возвращаемые значения

В случае успеха возвращает NK_EOK, иначе возвращает код ошибки.

nk_arena_capacity()

Назначение

Позволяет получить размер арены.

Параметры

  • [in] self – указатель на дескриптор арены.

Возвращаемые значения

Размер арены в байтах.

Дополнительные сведения

Если параметр имеет значение NK_NULL, возвращает 0.

nk_arena_validate()

Назначение

Проверяет, является ли корректным дескриптор участка арены.

Параметры

  • [in] type – тип объектов, для которых предназначен участок арены.
  • [in] arena – указатель на дескриптор арены.
  • [in] ptr – указатель на дескриптор участка арены.

Значения макроса

Имеет значение 0 при ненулевом размере арены, если выполняются все следующие условия:

  1. Смещение, указанное в дескрипторе участка арены, не превышает размер арены.
  2. Размер, указанный в дескрипторе участка арены, не превышает размер арены, уменьшенный на смещение, указанное в дескрипторе участка арены.
  3. Размер, указанный в дескрипторе участка арены, кратен размеру типа объектов, для которых предназначен этот участок арены.

Имеет значение 0 при нулевом размере арены, если выполняются все следующие условия:

  1. Смещение, указанное в дескрипторе участка арены, равно нулю.
  2. Размер, указанный в дескрипторе участка арены, равен нулю.

Имеет значение -1 при нарушении хотя бы одного условия как в случае с ненулевым, так и в случае с нулевым размером арены, или если параметр ptr имеет значение NK_NULL.

__nk_arena_validate()

Назначение

Проверяет, является ли корректным дескриптор участка арены.

Параметры

  • [in] self – указатель на дескриптор арены.
  • [in] ptr – указатель на дескриптор участка арены.

Возвращаемые значения

Возвращает 0 при ненулевом размере арены, если выполняются все следующие условия:

  1. Смещение, указанное в дескрипторе участка арены, не превышает размер арены.
  2. Размер, указанный в дескрипторе участка арены, не превышает размер арены, уменьшенный на смещение, указанное в дескрипторе участка арены.

Возвращает 0 при нулевом размере арены, если выполняются все следующие условия:

  1. Смещение, указанное в дескрипторе участка арены, равно нулю.
  2. Размер, указанный в дескрипторе участка арены, равен нулю.

Возвращает -1 при нарушении хотя бы одного условия как в случае с ненулевым, так и в случае с нулевым размером арены, или если параметр ptr имеет значение NK_NULL.

__nk_arena_get()

Назначение

Позволяет получить указатель на участок арены и размер этого участка.

Параметры

  • [in] self – указатель на дескриптор арены.
  • [in] ptr – указатель на дескриптор участка арены.
  • [out] size – размер участка арены в байтах.

Возвращаемые значения

Указатель на участок арены или NK_NULL, если хотя бы один параметр имеет значение NK_NULL.

nk_arena_allocated_size()

Назначение

Позволяет получить размер использованной части арены.

Параметры

  • [in] self – указатель на дескриптор арены.

Возвращаемые значения

Размер использованной части арены в байтах.

Дополнительные сведения

Если параметр имеет значение NK_NULL, возвращает 0.

nk_arena_store()

Назначение

Резервирует участок арены для заданного числа объектов заданного типа и копирует эти объекты в зарезервированный участок.

Параметры

  • [in] type – тип объектов, которые нужно скопировать в участок арены.
  • [in,out] arena – указатель на дескриптор арены.
  • [out] ptr – указатель не дескриптор участка арены.
  • [in] src – указатель на буфер с объектами, которые нужно скопировать в участок арены.
  • [in] count – число объектов, которые нужно скопировать в участок арены.

Значения макроса

В случае успеха имеет значение 0, иначе имеет значение -1.

__nk_arena_store()

Назначение

Резервирует участок арены с заданным выравниванием для данных заданного размера и копирует эти данные в зарезервированный участок.

Параметры

  • [in,out] self – указатель на дескриптор арены.
  • [out] ptr – указатель не дескриптор участка арены.
  • [in] src – указатель на буфер с данными, которые нужно скопировать в участок арены.
  • [in] size – размер данных, которые нужно скопировать в участок арены, в байтах.
  • [in] align – значение, задающее выравнивание участка арены. Адрес участка арены может быть невыравненным (align=1) или выравненным (align=2,4,...,2^N) на границу 2^N-байтовой последовательности (например, двухбайтовой, четырехбайтовой).

Возвращаемые значения

В случае успеха возвращает 0, иначе возвращает -1.

nk_arena_init_final()

Назначение

Инициализирует дескриптор арены, содержащей только один участок.

Параметры

  • [out] arena – указатель на дескриптор арены.
  • [out] ptr – указатель на дескриптор участка арены.
  • [in] start – указатель на начало арены.
  • [in] count – число объектов, для которых предназначен участок арены.

Значения макроса

Нет.

nk_arena_alloc()

Назначение

Резервирует участок арены для заданного числа объектов заданного типа.

Параметры

  • [in] type – тип объектов, для которых предназначен участок арены.
  • [in,out] arena – указатель на дескриптор арены.
  • [out] ptr – указатель на дескриптор участка арены.
  • [in] count – число объектов, для которых предназначен участок арены.

Значения макроса

В случае успеха имеет значение адреса зарезервированного участка арены, иначе имеет значение NK_NULL.

nk_arena_get()

Назначение

Позволяет получить адрес участка арены и число объектов заданного типа, которое вмещается в этом участке.

Параметры

  • [in] type – тип объектов, для которых предназначен участок арены.
  • [in] arena – указатель на дескриптор арены.
  • [in] ptr – указатель на дескриптор участка арены.
  • [out] count – указатель на число объектов, которое вмещается в участке арены.

Значения макроса

В случае успеха имеет значение адреса участка арены, иначе имеет значение NK_NULL.

Дополнительные сведения

Если размер участка арены не кратен размеру типа объектов, для которых этот участок предназначен, имеет значение NK_NULL.

nk_arena_shrink()

Назначение

Уменьшает размер участка арены.

Параметры

  • [in] type – тип объектов, для которых предназначен уменьшенный участок арены.
  • [in,out] arena – указатель на дескриптор арены.
  • [in,out] ptr – указатель на дескриптор участка арены.
  • [in] count – число объектов, для которых предназначен уменьшенный участок арены.

Значения макроса

В случае успеха имеет значение адреса уменьшенного участка арены, иначе имеет значение NK_NULL.

Дополнительные сведения

Если требуемый размер участка арены превышает текущий, имеет значение NK_NULL.

Если выравнивание участка арены, который нужно уменьшить, не удовлетворяет типу объектов, для которых предназначен уменьшенный участок, имеет значение NK_NULL.

Если участок арены, который нужно уменьшить, является последним в арене, то освободившаяся часть этого участка становится доступной для резервирования последующих участков.

_nk_arena_shrink()

Назначение

Уменьшает размер участка арены.

Параметры

  • [in,out] self – указатель на дескриптор арены.
  • [in,out] ptr – указатель на дескриптор участка арены.
  • [in] size – размер уменьшенного участка арены в байтах.
  • [in] align – значение, используемое функцией для проверки выравнивания участка арены, который нужно уменьшить. Адрес участка арены может быть невыравненным (align=1) или выравненным (align=2,4,...,2^N) на границу 2^N-байтовой последовательности (например, двухбайтовой, четырехбайтовой).

Возвращаемые значения

В случае успеха возвращает адрес уменьшенного участка арены, иначе возвращает NK_NULL.

Дополнительные сведения

Если требуемый размер участка арены превышает текущий, возвращает NK_NULL.

Если выравнивание участка арены, который нужно уменьшить, не удовлетворяет заданному выравниванию, возвращает NK_NULL.

Если участок арены, который нужно уменьшить, является последним в арене, то освободившаяся часть этого участка становится доступной для резервирования последующих участков.

В начало
[Topic ipc_arena]

Транспортный код на языке C++

Перед чтением этого раздела, нужно ознакомиться со сведениями о механизме IPC в KasperskyOS и об IDL-, CDL-, EDL-описаниях.

Чтобы реализовать взаимодействие процессов, необходим транспортный код, отвечающий за формирование, отправку, прием и обработку IPC-сообщений.

У разработчика решения на базе KasperskyOS нет необходимости самостоятельно писать транспортный код. Вместо этого можно использовать специальные инструменты и библиотеки, поставляемые в составе KasperskyOS SDK. Эти библиотеки позволяют разработчику компонента решения сгенерировать транспортный код на основе IDL-, CDL-, EDL-описаний, относящихся к этому компоненту.

Транспортный код

Для генерации транспортного кода на языке C++ в составе KasperskyOS SDK поставляется компилятор nkppmeta.

Компилятор nkppmeta позволяет генерировать транспортные C++ прокси-объекты (proxy) и стабы (stub) для использования как клиентом, так и сервером.

Прокси-объекты используются клиентом для упаковки параметров вызываемого метода в IPC-запрос, выполнения IPC-запроса и распаковки IPC-ответа.

Стабы используются сервером для распаковки параметров из IPC-запроса, диспетчеризации вызова на соответствующую реализацию метода и упаковки IPC-ответа.

Генерация транспортного кода для разработки на C++

Для генерации транспортных прокси-объектов и стабов с помощью генератора nkppmeta при сборке решения используются CMake-команды add_nk_idl(), add_nk_cdl() и add_nk_edl().

Типы С++ в файле *.idl.cpp.h

Каждый интерфейс определяется в IDL-описании. Это описание задает имя интерфейса, сигнатуры интерфейсных методов и типы данных для параметров интерфейсных методов.

Для генерации транспортного кода при сборке решения используются CMake-команда add_nk_idl(), которая создает CMake-цель для генерации заголовочных файлов для заданного IDL-файла при помощи компилятора nkppmeta.

Генерируемые заголовочные файлы содержат представление на языке C++ для интерфейса и типов данных, описанных в IDL-файле, а также методы, необходимые для использования прокси-объектов и стабов.

Соответствие типов данных, объявленных в IDL-файле, типам C++ приведены в таблице ниже.

Соответствие типов IDL типам C++

Тип IDL

Тип С++

SInt8

int8_t

SInt16

int16_t

SInt32

int32_t

SInt64

int64_t

UInt8

uint8_t

UInt16

uint16_t

UInt32

uint32_t

UInt64

uint64_t

Handle

Handle (определен в coresrv/handle/handletype.h)

string

std::string

union

std::variant

struct

struct

array

std::array

sequence

std::vector

bytes

std::vector<std::byte>

Работа с транспортным кодом на C++

Сценарии разработки клиента и сервера, которые обмениваются IPC-сообщениями, представлены в разделах "Статическое создание IPC-каналов при разработке на языке C++" и "Динамическое создание IPC-каналов при разработке на языке C++"

В начало
[Topic cpp_proxy_stubs]

Статическое создание IPC-каналов при разработке на языке C++

Чтобы реализовать клиентскую программу, вызывающую метод службы, предоставляемой серверной программой, необходимо:

  1. Подключить сгенерированный заголовочный файл описания (*.edl.cpp.h) клиентской программы.
  2. Подключить сгенерированные заголовочные файлы описаний используемых интерфейсов (*.idl.cpp.h).
  3. Подключить заголовочные файлы:
    • /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/application.h
    • /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/api.h
    • /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/connect_static_channel.h
  4. Инициализировать объект приложения, вызвав функцию kosipc::MakeApplicationAutodetect(). (Также можно использовать функции kosipc::MakeApplication() и kosipc::MakeApplicationPureClient().)
  5. Получить клиентский IPC-дескриптор канала и идентификатор службы (riid) вызвав функцию kosipc::ConnectStaticChannel().

    Функция принимает имя IPC-канала (из файла init.yaml) и квалифицированное имя службы (из CDL- и EDL-описаний компонента решения).

  6. Инициализировать прокси-объект для используемой службы, вызвав функцию MakeProxy().

Пример

// Создание и инициализация объекта приложения kosipc::Application app = kosipc::MakeApplicationAutodetect(); // Создание и инициализация прокси-объекта auto proxy = app.MakeProxy<IDLInterface>( kosipc::ConnectStaticChannel(channelName, endpointName)) // Вызов метода требуемой службы proxy->DoSomeWork();

Чтобы реализовать серверную программу, предоставляющую службы другим программам, необходимо:

  1. Подключить сгенерированный заголовочный файл *.edl.cpp.h, содержащий описание компонентной структуры программы, включая все предоставляемые службы.
  2. Подключить заголовочные файлы:
    • /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/event_loop.h
    • /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/api.h
    • /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/serve_static_channel.h
  3. Создать классы, содержащие реализации интерфейсов, которые данная программа и её компоненты предоставляют в виде служб.
  4. Инициализировать объект приложения, вызвав функцию kosipc::MakeApplicationAutodetect().
  5. Инициализировать структуру kosipc::components::Root, которая описывает компонентную структуру программы и описания интерфейсов всех предоставляемых программой служб.
  6. Связать поля структуры kosipc::components::Root с объектами, реализующими соответствующие службы.

    Поля структуры Root повторяют иерархию компонентов и служб, заданную совокупностью CDL- и EDL-файлов.

  7. Получить серверный IPC-дескриптор канала, вызвав функцию ServeStaticChannel().

    Функция принимает имя IPC-канала (из файла init.yaml) и структуру, созданную на шаге 5.

  8. Создать объект kosipc::EventLoop, вызвав функцию MakeEventLoop().
  9. Запустить цикл диспетчеризации входящих IPC-сообщений, вызвав метод Run() объекта kosipc::EventLoop.

Пример

// Создание объектов классов, которые реализуют интерфейсы, // предоставляемые сервером в виде служб MyIDLInterfaceImp_1 impl_1; MyIDLInterfaceImp_2 impl_2; // Создание и инициализация объекта приложения kosipc::Application app = kosipc::MakeApplicationAutodetect(); // Создание и инициализация объекта root, описывающего // компоненты и службы сервера kosipc::components::Root root; // Связывание объекта root с объектами классов, реализующими службы сервера root.component1.endpoint1 = &impl_1; root.component2.endpoint2 = &impl_2; // Создание и инициализация объекта, который реализует // цикл диспетчеризации входящих IPC-сообщений kosipc::EventLoop loop = app.MakeEventLoop(ServeStaticChannel(channelName, root)); // Запуск цикла в текущем потоке loop.Run();
В начало
[Topic static_IPC_kosipc]

Динамическое создание IPC-каналов при разработке на языке C++

Динамическое создание IPC-канала на стороне клиента включает следующие шаги:

  1. Подключить к клиентской программе сгенерированный заголовочный файл описания (*.edl.cpp.h).
  2. Подключить сгенерированные заголовочные файлы описаний используемых интерфейсов (*.idl.cpp.h).
  3. Подключить заголовочные файлы:
    • /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/application.h
    • /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/make_application.h
    • /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/connect_dynamic_channel.h
  4. Получить указатели на имя сервера и квалифицированное имя службы с помощью сервера имен – специального сервиса ядра, представленного программой NameServer. Для этого необходимо подключиться к серверу имен вызовом функции NsCreate() и найти сервер, предоставляющий требуемую службу, используя функцию NsEnumServices(). Подробнее см. "Динамическое создание IPC-каналов (cm_api.h, ns_api.h)".
  5. Создать объект приложения, вызвав функцию kosipc::MakeApplicationAutodetect(). (Также можно использовать функции kosipc::MakeApplication() и kosipc::MakeApplicationPureClient().)
  6. Создать прокси-объект для требуемой службы, вызвав функцию MakeProxy(). В качестве входного параметра функции MakeProxy() использовать вызов функции kosipc::ConnectDynamicChannel(). В функцию kosipc::ConnectDynamicChannel() передать указатели на имя сервера и квалифицированное имя службы, полученные на шаге 4.

После успешной инициализации прокси-объекта клиенту доступен вызов методов требуемой службы.

Пример

NsHandle ns; // Подключение к серверу имен Retcode rc = NsCreate(RTL_NULL, INFINITE_TIMEOUT, &ns); char serverName[kl_core_Types_UCoreStringSize]; char endpointName[kl_core_Types_UCoreStringSize]; // Получение указателей на имя сервера и квалифицированное имя службы rc = NsEnumServices( ns, interfaceName, 0, serverName, kl_core_Types_UCoreStringSize, endpointName, kl_core_Types_UCoreStringSize); // Создание и инициализация объекта приложения kosipc::Application app = kosipc::MakeApplicationAutodetect(); // Создание и инициализация прокси-объекта auto proxy = app.MakeProxy<IDLInterface>( kosipc::ConnectDynamicChannel(serverName, endpointName)) // Вызов метода требуемой службы proxy->DoSomeWork();

Динамическое создание IPC-канала на стороне сервера включает следующие шаги:

  1. Подключить к серверной программе сгенерированный заголовочный файл (*.edl.cpp.h), содержащий описание компонентной структуры сервера, включая все предоставляемые службы.
  2. Подключить заголовочные файлы:
    • /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/application.h
    • /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/event_loop.h
    • /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/make_application.h
    • /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/root_component.h
    • /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/serve_dynamic_channel.h
    • /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/simple_connection_acceptor.h
  3. Создать классы, содержащие реализации интерфейсов, которые сервер предоставляет в виде служб. Создать и инициализировать объекты этих классов.
  4. Создать объект приложения, вызвав функцию kosipc::MakeApplicationAutodetect().
  5. Создать и инициализировать объект класса kosipc::components::Root, который описывает структуру компонентов и служб сервера. Эта структура генерируется из описаний в CDL- и EDL-файлах.
  6. Cвязать объект класса kosipc::components::Root с объектами классов, созданными на шаге 3.
  7. Создать и инициализировать объект класса kosipc::EventLoop, который реализует цикл диспетчеризации входящих IPC-сообщений, вызвав функцию MakeEventLoop(). В качестве входного параметра функции MakeEventLoop() использовать вызов функции ServeDynamicChannel(). В функцию ServeDynamicChannel() передать объект класса kosipc::components::Root, созданный на шаге 5.
  8. Запустить цикл диспетчеризации входящих IPC-сообщений в отдельном потоке, вызвав метод Run() объекта kosipc::EventLoop.
  9. Создать и инициализировать объект, который реализует обработчик приема входящих запросов на динамическое создание IPC-канала.

    При создании объекта можно использовать класс kosipc::SimpleConnectionAcceptor, который является стандартной реализацией интерфейса kosipc::IConnectionAcceptor. (Интерфейс kosipc::IConnectionAcceptor определен в файле /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/connection_acceptor.h.) В этом случае обработчик будет реализовать следующую логику: если запрашиваемая клиентом служба опубликована на сервере, то запрос от клиента будет принят, иначе отклонен.

    Если необходимо создать собственный обработчик, то следует реализовать свою логику обработки запросов в методе OnConnectionRequest(), унаследованном от интерфейса kosipc::IConnectionAcceptor. Этот метод будет вызываться сервером при получении от клиента запроса на динамическое создание IPC-канала.

  10. Создать объект класса kosipc::EventLoop, который реализует цикл приема входящих запросов на динамическое создание IPC-канала, вызвав функцию MakeEventLoop(). В качестве входного параметра функции MakeEventLoop() использовать вызов функции ServeConnectionRequests(). В функцию ServeConnectionRequests() передать объект, созданный на шаге 9.

    Цикл приема входящих запросов на динамическое создание IPC-канала может быть только один. Цикл должен работать в одном потоке. Цикл приема входящих запросов на динамическое создание IPC-канала должен быть создан после создания цикла диспетчеризации входящих IPC-сообщений (см. на шаг 7).

  11. Запустить цикл приема входящих запросов на динамическое соединение в текущем потоке, вызвав метод Run() объекта kosipc::EventLoop.

Пример

// Создание объектов классов, которые реализуют интерфейсы, // предоставляемые сервером в виде служб MyIDLInterfaceImp_1 impl_1; MyIDLInterfaceImp_2 impl_2; // Создание и инициализация объекта приложения kosipc::Application app = kosipc::MakeApplicationAutodetect(); // Создание и инициализация объекта root, описывающего // компоненты и службы сервера kosipc::components::Root root; // Связывание объекта root с объектами классов, реализующими службы сервера. // Поля объекта root повторяют описание компонентов и служб, // заданную совокупностью CDL- и EDL-файлов. root.component1.endpoint1 = &impl_1; root.component2.endpoint2 = &impl_2; // Создание и инициализация объекта, который реализует // цикл диспетчеризации входящих IPC-сообщений kosipc::EventLoop loopDynamicChannel = app.MakeEventLoop(ServeDynamicChannel(root)); // Запуск цикла диспетчеризации входящих IPC-сообщений в отдельном потоке std::thread dynChannelThread( [&loopDynamicChannel]() { loopDynamicChannel.Run(); } ); // Создание объекта, реализующего стандартный обработчик приема входящих запросов // на динамическое создание IPC-канала kosipc::SimpleConnectionAcceptor acceptor(root); // Создание объекта, реализующего цикл приема входящих запросов // на динамическое создание IPC-канала kosipc::EventLoop loopDynamicChannel = app.MakeEventLoop(ServeConnectionRequests(&acceptor)); // Запуск цикла приема входящих запросов на динамическое создание IPC-канала в текущем потоке loopConnectionReq.Run();

При необходимости можно создать и инициализировать несколько объектов класса kosipc::components::Root, объединенных в список объектов типа ServiceList с помощью метода AddServices(). Использование нескольких объектов позволяет, например, разделять компоненты и службы сервера на группы или публиковать службы под разными именами.

Пример

// Создание и инициализация объекта group_1 kosipc::components::Root group_1; group_1.component1.endpoint1 = &impl_1; group_1.component2.endpoint2 = &impl_2; // Создание и инициализация объекта group_2 kosipc::components::Root group_2; group_2.component1.endpoint1 = &impl_3; group_2.component2.endpoint2 = &impl_4; // Создание и инициализация объекта group_3 kosipc::components::Root group_3; group_3.component1.endoint1 = &impl_5; // Создание списка объектов ServiceList endpoints; endpoints.AddServices(group_1); endpoints.AddServices(group_2); endpoints.AddServices(group_3.component1.endpoint1, "SomeCustomEndpointName"); // Создание объекта, реализующего обработчик приема входящих запросов // на динамическое создание IPC-канала kosipc::SimpleConnectionAcceptor acceptor(std::move(endpoints)); // Создание объекта, реализующего цикл приема входящих запросов // на динамическое создание IPC-канала kosipc::EventLoop loopDynamicChannel = app.MakeEventLoop(ServeConnectionRequests(&acceptor));
В начало
[Topic dynamic_IPC_kosipc]