KasperskyOS Community Edition 1.0
Динамическое создание IPC-каналов

Возможность динамического создания IPC-каналов позволяет изменять топологию взаимодействия сущностей "на лету". Это требуется, если неизвестно, какая именно сущность-сервер станет поставщиком ресурса, требуемого сущности-клиенту. Например, может быть неизвестно, на какой именно накопитель нужно будет записывать данные.

Динамически созданный IPC-канал, в отличие от статически созданного, имеет следующие особенности:

  • создается между запущенными сущностями;
  • создается совместно сущностью-клиентом и сущностью-сервером;
  • предусматривает удаление;
  • предусматривает использование сервера имен (сущности kl.core.NameServer), который обеспечивает передачу сведений о доступных реализациях интерфейсов от сущностей-серверов к сущностям-клиентам.

При динамическом создании IPC-канала используются функции:

  • интерфейса сервера имен (Name Server);
  • менеджера соединений (Connection Manager).

Интерфейс сервера имен представлен для пользователя следующими файлами:

  • coresrv/ns/ns_api.h – заголовочный файл библиотеки libkos;
  • coresrv/ns/NameServer.idl – описание IPC-интерфейса сервера имен на языке IDL.

Менеджер соединений представлен для пользователя следующими файлами:

  • coresrv/cm/cm_api.h – заголовочный файл библиотеки libkos;
  • services/cm/CM.idl – описание IPC-интерфейса менеджера соединений на языке IDL.

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

  1. Запускаются сущность-клиент, сущность-сервер и сервер имен.
  2. Сущность-сервер подключается к серверу имен функцией NsCreate() и публикует имя сущности-сервера, имя интерфейса и имя реализации интерфейса функцией NsPublishService().
  3. Сущность-клиент подключается к серверу имен функцией NsCreate() и выполняет поиск имени сущности-сервера и имени реализации интерфейса по имени интерфейса функцией NsEnumServices().
  4. Сущность-клиент запрашивает доступ к реализации интерфейса функцией KnCmConnect(), передавая ей в качестве аргументов найденные имя сущности-сервера и имя реализации интерфейса.
  5. Сущность-сервер вызывает функцию KnCmListen() для проверки наличия запроса доступа к предоставляемой реализации интерфейса от сущности-клиента.
  6. Сущность-сервер принимает запрос доступа к предоставляемой реализации интерфейса функцией KnCmAccept(), передавая ей в качестве аргументов имя сущности-клиента и имя реализации интерфейса, которые получены функцией KnCmListen().

Для использования сервера имен политика безопасности решения должна разрешать взаимодействие сущности kl.core.NameServer с сущностями, между которыми могут динамически создаваться IPC-каналы.

Сущность-сервер может снимать с публикации на сервере имен ранее опубликованные реализации интерфейсов функцией NsUnPublishService().

Сущность-сервер может отклонять запросы доступа к реализациям интерфейсов функцией KnCmDrop().

ns_api.h (фрагмент)

/**

* Функция осуществляет попытку подключиться к серверу имен name

* в течение msecs миллисекунд. Если параметр name имеет значение

* RTL_NULL, функция пытается подключиться к серверу имен ns

* (серверу имен по умолчанию). Выходной параметр ns содержит дескриптор

* соединения с сервером имен.

* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.

*/

Retcode NsCreate(const char *name, rtl_uint32_t msecs, NsHandle *ns);

/**

* Функция публикует реализацию интерфейса на сервере имен.

* Параметр ns задает дескриптор соединения с сервером имен.

* Параметр server задает имя сущности-сервера (например, ata).

* Параметр type задает имя публикуемого интерфейса (например, IBlkDev).

* Параметр service задает имя реализации публикуемого интерфейса

* (например, blkdev.blkdev).

* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.

*/

Retcode NsPublishService(NsHandle ns, const char *type, const char *server,

const char *service);

/**

* Функция снимает с публикации реализацию интерфейса на сервере имен.

* Параметр ns задает дескриптор соединения с сервером имен.

* Параметр server задает имя сущности-сервера. Параметр type задает имя

* опубликованного интерфейса. Параметр service задает имя реализации

* опубликованного интерфейса.

* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.

*/

Retcode NsUnPublishService( NsHandle ns, const char *type, const char *server,

const char *service);

/**

* Функция перечисляет реализации интерфейса, опубликованные на сервере

* имен. Параметр ns задает дескриптор соединения с сервером имен.

* Параметр type задает имя требуемого интерфейса. Параметр index

* задает индекс для перечисления реализаций интерфейса.

* Выходной параметр server содержит имя сущности-сервера, предоставляющей

* реализацию интерфейса. Выходной параметр service содержит имя реализации

* интерфейса.

* Например, перечисление реализаций интерфейса IBlkDev осуществляется

* следующим образом.

* rc = NsEnumServices(ns, "IBlkDev", 0, outServerName, outServiceName);

* rc = NsEnumServices(ns, "IBlkDev", 1, outServerName, outServiceName);

* ...

* rc = NsEnumServices(ns, "IBlkDev", N, outServerName, outServiceName);

* Вызовы функции с инкрементированием индекса продолжается до тех пор,

* пока функция не вернет rcResourceNotFound.

* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.

*/

Retcode NsEnumServices(NsHandle ns, const char *type, unsigned index,

char *server, char *service);

cm_api.h (фрагмент)

/**

* Функция запрашивает доступ к реализации интерфейса с именем service,

* предоставляемой сущностью-сервером server. Параметр msecs задает время

* ожидания принятия запроса сущностью-сервером в миллисекундах. Выходной

* параметр endpoint содержит клиентский IPC-дескриптор. Выходной параметр

* rsid содержит идентификатор реализации интерфейса.

* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.

*/

Retcode KnCmConnect(const char *server, const char *service,

rtl_uint32_t msecs, Handle *endpoint,

rtl_uint32_t *rsid);

/**

* Функция проверяет наличие запроса доступа к реализации интерфейса

* с именем filter. Если параметр filter имеет значение RTL_NULL,

* проверяется наличие запроса доступа к любой реализации

* интерфейса. Параметр msecs задает время ожидания запроса

* в миллисекундах. Выходной параметр client содержит имя

* сущности-клиента. Выходной параметр service содержит имя реализации

* интерфейса.

* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.

*/

Retcode KnCmListen(const char *filter, rtl_uint32_t msecs, char *client,

char *service);

/**

* Функция отклоняет запрос доступа к реализации интерфейса

* с именем service от сущности-клиента client.

* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.

*/

Retcode KnCmDrop(const char *client, const char *service);

/**

* Функция принимает запрос доступа к реализации интерфейса

* с именем service от сущности-клиента client. Параметр rsid задает

* идентификатор реализации интерфейса. Параметр listener задает

* слушающий дескриптор. Если параметр listener имеет значение

* INVALID_HANDLE, создается новый слушающий дескриптор.

* Выходной параметр endpoint содержит серверный IPC-дескриптор.

* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.

*/

Retcode KnCmAccept(const char *client, const char *service, rtl_uint32_t rsid,

Handle listener, Handle *endpoint);

Параметр filter функции KnCmListen() является зарезервированным и не влияет на ее поведение. Поведение функции соответствует значению RTL_NULL параметра filter.

Слушающий дескриптор – серверный IPC-дескриптор с расширенными правами. Он создается при вызове функции KnCmAccept() с аргументом INVALID_HANDLE в параметре listener. Если при вызове функции KnCmAccept() передать ей слушающий дескриптор, то созданный серверный IPC-дескриптор обеспечит возможность получать запросы по всем IPC-каналам, ассоциированным с этим слушающим дескриптором. (Первый IPC-канал, ассоциированный со слушающим дескриптором, создается при вызове функции KnCmAccept() с аргументом INVALID_HANDLE в параметре listener. Второй и последующие IPC-каналы, ассоциированные со слушающим дескриптором, создаются при втором и последующих вызовах функции KnCmAccept() с передачей ей слушающего дескриптора, полученного при первом вызове.)

Слушающий дескриптор используется также при статическом создании IPC-каналов. Он создается при вызове функции KnHandleConnectEx() с аргументом INVALID_HANDLE в параметре srListener. Если при вызове функции KnHandleConnectEx() передать ей слушающий дескриптор, то созданный серверный IPC-дескриптор обеспечит возможность получать запросы по всем IPC-каналам, ассоциированным с этим слушающим дескриптором. Ассоциация слушающего дескриптора с несколькими IPC-каналами происходит, когда для одной сущности-сервера создается несколько IPC-каналов с одинаковыми именами.

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