KasperskyOS Community Edition 1.3

Содержание

[Topic developing][Topic sc_application_start]

Обзор: Einit и init.yaml

Инициализирующая программа Einit

При запуске решения ядро KasperskyOS находит в образе решения и запускает исполняемый файл с именем Einit, то есть инициализирующую программу. Инициализирующая программа выполняет следующие действия:

  • создает и запускает процессы при запуске решения;
  • создает IPC-каналы между процессами при запуске решения (статически создает IPC-каналы).

Процесс инициализирующей программы имеет класс Einit.

Генерация исходного кода инициализирующей программы

В составе KasperskyOS SDK поставляется утилита einit, которая позволяет генерировать исходный код инициализирующей программы на языке C. Стандартным способом использования утилиты einit является интеграция ее вызова в один из шагов сборочного скрипта, в результате которого генерируется файл einit.c, содержащий исходный код инициализирующей программы. На одном из следующих шагов сборочного скрипта необходимо скомпилировать файл einit.c в исполняемый файл Einit и включить в образ решения.

Для инициализирующей программы не требуется создавать файлы формальной спецификации. Эти файлы поставляются в составе KasperskyOS SDK и автоматически применяются при сборке решения. Однако класс процесса Einit должен быть указан в файле security.psl.

Утилита einit генерирует исходный код инициализирующей программы основе init-описания, представляющего собой текстовый файл, который обычно имеет имя init.yaml.

Синтаксис init.yaml

Init-описание содержит данные в формате YAML, которые идентифицируют:

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

Эти данные представляют собой словарь с ключом entities, содержащий список словарей процессов. Ключи словаря процесса приведены в таблице ниже.

Ключи словаря процесса в init-описании

Ключ

Обязательный

Значение

name

Да

Имя класса процесса (из EDL-описания).

task

Нет

Имя процесса. Если его не указать, то будет взято имя класса процесса. У каждого процесса должно быть уникальное имя.

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

path

Нет

Имя исполняемого файла в ROMFS (в образе решения). Если его не указать, то будет взято имя класса процесса без префиксов и точек. Например, процессы классов Client и net.Client, для которых не указано имя исполняемого файла, будут запущены из файла Client.

Можно запустить несколько процессов из одного исполняемого файла.

connections

Нет

Список словарей IPC-каналов процесса. Этот список задает статически создаваемые IPC-каналы, клиентскими IPC-дескрипторами которых будет владеть процесс. По умолчанию этот список пуст. (Помимо статически создаваемых IPC-каналов процессы могут использовать динамически создаваемые IPC-каналы.)

args

Нет

Список параметров запуска программы (параметры функции main()). Максимальный размер одного элемента списка составляет 1024 байта.

env

Нет

Словарь переменных окружения программы. Ключами в этом словаре являются имена переменных окружения. Максимальный размер значения переменной окружения составляет 65524 байта.

Ключи словаря IPC-канала процесса приведены в таблице ниже.

Ключи словаря IPC-канала в init-описании

Ключ

Обязательный

Значение

id

Да

Имя IPC-канала, которое может быть задано как конкретным значением, так и ссылкой вида

{var: <имя константы>, include: <путь к заголовочному файлу>}.

target

Да

Имя процесса, который будет владеть серверным дескриптором IPC-канала.

В начало
[Topic einit_overview]

Примеры init-описаний

Здесь приведены примеры init-описаний, демонстрирующие различные аспекты запуска процессов.

Система сборки может автоматически создавать init-описание на основе шаблона init.yaml.in.

Запуск клиента и сервера и создание IPC-канала между ними

В этом примере будут запущены процесс класса Client и процесс класса Server. Имена процессов не указаны, поэтому они будут совпадать с именами классов процессов. Имена исполняемых файлов также не указаны, они также будут совпадать с именами классов процессов. Процессы будут соединены IPC-каналом с именем server_connection.

init.yaml

entities: - name: Client connections: - target: Server id: server_connection - name: Server

Запуск процессов из заданных исполняемых файлов

В этом примере будут запущены процесс класса Client из исполняемого файла с именем cl, процесс класса ClientServer из исполняемого файла с именем csr и процесс класса MainServer из исполняемого файла с именем msr. Имена процессов не указаны, поэтому они будут совпадать с именами классов процессов.

init.yaml

entities: - name: Client path: cl - name: ClientServer path: csr - name: MainServer path: msr

Запуск двух процессов из одного исполняемого файла

В этом примере будут запущены процесс класса Client из исполняемого файла с именем Client, а также два процесса классов MainServer и BkServer из исполняемого файла с именем srv. Имена процессов не указаны, поэтому они будут совпадать с именами классов процессов.

init.yaml

entities: - name: Client - name: MainServer path: srv - name: BkServer path: srv

Запуск двух серверов одного класса и клиента и создание IPC-каналов между клиентом и серверами

В этом примере будут запущены процесс класса Client (с именем Client) и два процесса класса Server с именами UserServer и PrivilegedServer. Клиент будет соединен с серверами IPC-каналами с именами server_connection_us и server_connection_ps. Имена исполняемых файлов не указаны, поэтому они будут совпадать с именами классов процессов.

init.yaml

entities: - name: Client connections: - id: server_connection_us target: UserServer - id: server_connection_ps target: PrivilegedServer - task: UserServer name: Server - task: PrivilegedServer name: Server

Установка параметров запуска и переменных окружения программ

В этом примере будут запущены процесс класса VfsFirst (с именем VfsFirst) и процесс класса VfsSecond (с именем VfsSecond). Программа, которая будет исполняться в контексте процесса VfsFirst, будет запущена с параметром -f /etc/fstab, а также получит переменную окружения ROOTFS со значением ramdisk0,0 / ext2 0 и переменную окружения UNMAP_ROMFS со значением 1. Программа, которая будет исполняться в контексте процесса VfsSecond, будет запущена с параметром -l devfs /dev devfs 0. Имена исполняемых файлов не указаны, поэтому они будут совпадать с именами классов процессов.

init.yaml

entities: - name: VfsFirst args: - -f - /etc/fstab env: ROOTFS: ramdisk0,0 / ext2 0 UNMAP_ROMFS: 1 - name: VfsSecond args: - -l - devfs /dev devfs 0
В начало
[Topic using_einit]

Запуск процессов с помощью системной программы ExecutionManager

Компонент ExecutionManager предоставляет интерфейс на языке C++ для создания, запуска и остановки процессов в решениях, построенных на базе KasperskyOS.

Интерфейс компонента ExecutionManager не подходит для использования в коде, написанном на языке C. Для управления процессами на языке C используйте интерфейс task.h библиотеки libkos.

Используя компонент ExecutionManager можно запускать процессы из:

  • исполняемого файла, для которого известно его расположение в файловой системе;
  • исполняемого файла, установленного компонентом PackageManager в решение на базе KasperskyOS из KPA-пакета.

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

Программный интерфейс компонента ExecutionManager описан в статье "Компонент ExecutionManager".

Сценарий использования компонента ExecutionManager

Здесь и далее клиентом называется приложение, использующее API компонента ExecutionManager для управления другими приложениями.

Типовой сценарий использования компонента ExecutionManager включает следующие шаги:

  1. Добавление программы ExecutionManager в решение. Чтобы добавить ExecutionManager в решение, необходимо:
    find_package (execution_manager REQUIRED) include_directories (${execution_manager_INCLUDE}) add_subdirectory (execution_manager)

    Для работы программы ExecutionManager необходима программа BlobContainer. Эта программа автоматически добавляется в решение при добавлении ExecutionManager.

    • Компонент ExecutionManager поставляется в составе SDK в виде набора статических библиотек и заголовочных файлов и собирается под конкретное решение с помощью CMake-команды create_execution_manager_entity() из CMake-библиотеки execution_manager.

      Чтобы собрать программу ExecutionManager, необходимо в корневой директории проекта создать директорию с именем execution_manager, а в ней создать файл CMakeLists.txt, в котором содержится команда create_execution_manager_entity().

      CMake-команда create_execution_manager_entity() принимает следующие параметры:

      Обязательный параметр ENTITY, в котором указывается имя исполняемого файла для программы ExecutionManager.

      Опциональные параметры:

      • DEPENDS - дополнительные зависимости для сборки программы ExecutionManager.
      • MAIN_CONN_NAME - имя IPC-канала для соединения с процессом ExecutionManager. Должно совпадать со значением переменной mainConnection при обращении к API ExecutionManager в коде клиента.
      • ROOT_PATH - путь к корневой директории для служебных файлов программы ExecutionManager, по умолчанию "/ROOT".
      • VFS_CLIENT_LIB - имя клиентской транспортной библиотеки для подключения программы ExecutionManager к программе VFS.
    include (execution_manager/create_execution_manager_entity) create_execution_manager_entity( ENTITY ExecMgrEntity MAIN_CONN_NAME ${ENTITY_NAME} ROOT_PATH "/root" VFS_CLIENT_LIB ${vfs_CLIENT_LIB})
    • При сборке решения (файл CMakeLists.txt для программы Einit) добавить следующие исполняемые файлы в образ решения:
      • исполняемый файл программы ExecutionManager;
      • исполняемый файл программы BlobContainer.
  2. Компоновка исполняемого файла клиента с клиентской прокси-библиотекой ExecutionManager, для чего необходимо в файле CMakeLists.txt для сборки клиента добавить следующую команду:
    target_link_libraries (<имя CMake-цели для сборки клиента> ${execution_manager_EXECMGR_PROXY})
  3. Добавление разрешений для необходимых событий в описание политики безопасности решения:
    1. Чтобы программа ExecutionManager могла запускать другие процессы, политика безопасности решения должна разрешать следующие взаимодействия для класса процессов execution_manager.ExecMgrEntity:
      • События безопасности вида execute для всех классов запускаемых процессов.
      • Доступ ко всем службам программы VFS.
      • Доступ ко всем службам программы BlobContainer.
      • Доступ к службам ядра Sync, Task, VMM, Thread, HAL, Handle, FS, Notice, CM и Profiler (их описания находятся в директории sysroot-*-kos/include/kl/core из состава SDK).
    2. Чтобы клиент мог обращаться к программе ExecutionManager, политика безопасности решения должна разрешать следующие взаимодействия для класса клиентского процесса:
      • Доступ к соответствующим службам программы ExecutionManager (их описания находятся в директории sysroot-*-kos/include/kl/execution_manager из состава SDK).
  4. Использование API программы ExecutionManager в коде клиента.

    Для этого необходимо использовать заголовочный файл component/execution_manager/kos_ipc/execution_manager_proxy.h. Подробнее см. "Компонент ExecutionManager".

В начало
[Topic app_static_start]

Обзор: программа Env

Системная программа Env предназначена для установки параметров запуска и переменных окружения программ. Если программа Env включена в решение, то процессы, соединенные IPC-каналом с процессом Env, при своем запуске автоматически отправляют IPC-запросы этой программе и получают параметры запуска и переменные окружения.

Использование системной программы Env является устаревшим способом установки параметров запуска и переменных окружения программ. Установку параметров запуска и переменных окружения программ нужно выполнять через файл init.yaml.in или init.yaml.

Если значение параметра запуска или переменной окружения программы задано как через программу Env, так и через файл init.yaml.in или init.yaml, то будет применяться значение, заданное через программу Env.

Чтобы использовать программу Env в решении, необходимо:

  1. Разработать код программы Env, используя макросы и функции из заголовочного файла sysroot-*-kos/include/env/env.h из состава KasperskyOS SDK.
  2. Собрать исполняемый файл программы Env, скомпоновав его с библиотекой env_server из состава KasperskyOS SDK.
  3. В init-описании указать, что необходимо запустить процесс Env и соединить с ним другие процессы (Env при этом является сервером). Имя IPC-канала задается макросом ENV_SERVICE_NAME, определенным в заголовочном файле env.h.
  4. Включить исполняемый файл Env в образ решения.

Исходный код программы Env

В исходном коде программы Env используются следующие макросы и функции из заголовочного файла env.h:

  • ENV_REGISTER_ARGS(name,argarr) – установить параметры запуска argarr для программы, которая будет исполняться в контексте процесса с именем name.
  • ENV_REGISTER_VARS(name,envarr) – установить переменные окружения envarr для программы, которая будет исполняться в контексте процесса с именем name.
  • ENV_REGISTER_PROGRAM_ENVIRONMENT(name,argarr,envarr) – установить параметры запуска argarr и переменные окружения envarr для программы, которая будет исполняться в контексте процесса с именем name.
  • envServerRun() – инициализировать серверную часть программы Env, чтобы она могла отвечать на IPC-запросы.

Примеры использования программы Env

В начало
[Topic env_overview]

Примеры установки параметров запуска и переменных окружения программ с помощью Env

Использование системной программы Env является устаревшим способом установки параметров запуска и переменных окружения программ. Установку параметров запуска и переменных окружения программ нужно выполнять через файл init.yaml.in или init.yaml.

Если значение параметра запуска или переменной окружения программы задано как через программу Env, так и через файл init.yaml.in или init.yaml, то будет применяться значение, заданное через программу Env.

Пример установки параметров запуска программы

Исходный код программы Env, которая при запуске процесса с именем NetVfs передаст ему два параметра запуска программы: -l devfs /dev devfs 0 и -l romfs /etc romfs ro:

env.c

#include <env/env.h> #include <stdlib.h> int main(int argc, char** argv) { const char* NetVfsArgs[] = { "-l", "devfs /dev devfs 0", "-l", "romfs /etc romfs ro" }; ENV_REGISTER_ARGS("NetVfs", NetVfsArgs); envServerRun(); return EXIT_SUCCESS; }

Пример установки переменных окружения программы

Исходный код программы Env, которая при запуске процесса с именем Vfs3 передаст ему две переменных окружения программы: ROOTFS=ramdisk0,0 / ext2 0 и UNMAP_ROMFS=1:

env.c

#include <env/env.h> #include <stdlib.h> int main(int argc, char** argv) { const char* Vfs3Envs[] = { "ROOTFS=ramdisk0,0 / ext2 0", "UNMAP_ROMFS=1" }; ENV_REGISTER_VARS("Vfs3", Vfs3Envs); envServerRun(); return EXIT_SUCCESS; }
В начало
[Topic using_env_app]

Файловые системы и сеть

В KasperskyOS работа с файловыми системами и сетью выполняется через отдельную системную программу, реализующую виртуальную файловую систему (англ. Virtual File System, VFS).

В составе SDK компонент VFS представлен набором исполняемых файлов, библиотек, файлов формальной спецификации и заголовочных файлов. Подробнее см. раздел "Состав компонента VFS".

Основной сценарий взаимодействия с системной программой VFS происходит следующим образом:

  1. Прикладная программа соединяется IPC-каналом с системной программой VFS и при сборке компонуется с клиентской библиотекой компонента VFS.
  2. В прикладном коде POSIX-вызовы для работы с файловыми системами и сетью преобразуются в вызовы функций клиентской библиотеки.

    Ввод и вывод в файловые дескрипторы для стандартных потоков ввода-вывода (stdin, stdout и stderr) также преобразуется в обращения к VFS. Если прикладная программа не скомпонована с клиентской библиотекой компонента VFS, то вывод в stdout невозможен. В таком случае возможен только вывод в стандартный поток ошибок (stderr), который в этом случае осуществляется без использования VFS через специальные методы ядра KasperskyOS.

  3. Клиентская библиотека выполняет IPC-запросы к системной программе VFS.
  4. Системная программа VFS принимает IPC-запросы и вызывает соответствующие реализации файловых систем (которые, в свою очередь могут выполнять IPC-запросы к драйверам устройств) или сетевые драйверы.
  5. После обработки запроса, системная программа VFS выполняет ответы на IPC-запросы прикладной программы.

Использование нескольких программ VFS

В решение можно добавить несколько копий системной программы VFS, разделив таким образом информационные потоки разных системных и прикладных программ. Также можно разделить информационные потоки в рамках одной прикладной программы. Подробнее см. "Разделение информационных потоков с помощью VFS-бэкендов".

Включение функциональности VFS в прикладную программу

Функциональность компонента VFS можно полностью включить в прикладную программу, избавляясь при этом от необходимости передавать каждый запрос через IPC. Подробнее см. "Включение функциональности VFS в программу".

При этом использование функциональности VFS по IPC позволяет разработчику решения:

  • контролировать вызовы методов для работы с сетью и файловыми системами с помощью политики безопасности решения;
  • соединить несколько клиентских программ с одной программой VFS;
  • соединить одну клиентскую программу с двумя программами VFS для раздельной работы с сетью и файловыми системами.

В этом разделе

Состав компонента VFS

Создание IPC-канала до VFS

Включение функциональности VFS в программу

Обзор: параметры запуска и переменные окружения VFS

Монтирование файловых систем при запуске VFS

Разделение информационных потоков с помощью VFS-бэкендов

Создание VFS-бэкенда

Динамическая настройка сетевого стека

В начало
[Topic sc_filesystems_and_net]

Состав компонента VFS

Компонент VFS реализует виртуальную файловую систему. В составе KasperskyOS SDK компонент VFS представлен набором исполняемых файлов, библиотек, файлов формальной спецификации и заголовочных файлов, позволяющих использовать файловые системы и/или сетевой стек.

Библиотеки VFS

CMake-пакет vfs содержит следующие библиотеки:

  • vfs_fs – содержит реализации файловых систем devfs, ramfs и ROMFS, а также позволяет добавить в VFS реализации других файловых систем.
  • vfs_net – содержит реализацию файловой системы devfs и сетевого стека.
  • vfs_imp – содержит библиотеки vfs_fs и vfs_net.
  • vfs_remote – клиентская транспортная библиотека, которая преобразует локальные вызовы в IPC-запросы к VFS и принимает IPC-ответы.
  • vfs_server – серверная транспортная библиотека VFS, которая принимает IPC-запросы, преобразует их в локальные вызовы и отправляет IPC-ответы.
  • vfs_local – используется для включения функциональности VFS в программу.

Исполняемые файлы VFS

CMake-пакет precompiled_vfs содержит следующие исполняемые файлы:

  • VfsRamFs;
  • VfsSdCardFs;
  • VfsNet.

Исполняемые файлы VfsRamFs и VfsSdCardFs включают в себя библиотеки vfs_server, vfs_fs, vfat и lwext4. Исполняемый файл VfsNet включает в себя библиотеки vfs_server и vfs_imp.

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

Файлы формальной спецификации и заголовочные файлы VFS

В директории sysroot-*-kos/include/kl из состава KasperskyOS SDK находятся следующие файлы VFS:

  • файлы формальной спецификации VfsRamFs.edl, VfsSdCardFs.edl, VfsNet.edl и VfsEntity.edl и сгенерированные из них заголовочные файлы;
  • файл формальной спецификации Vfs.cdl и сгенерированный из него заголовочный файл Vfs.cdl.h;
  • файлы формальной спецификации Vfs*.idl и сгенерированные из них заголовочные файлы.

API библиотеки libc, поддерживаемый VFS

Функциональность VFS доступна программам через API, предоставляемый библиотекой libc.

Функции, реализуемые библиотеками vfs_fs и vfs_net, приведены в таблицах ниже. Символом * отмечены функции, которые включаются в библиотеку vfs_fs опционально (в зависимости от параметров сборки библиотеки).

Функции, реализуемые библиотекой vfs_fs

mount()

unlink()

ftruncate()

lsetxattr()*

umount()

rmdir()

chdir()

fsetxattr()*

open()

mkdir()

fchdir()

getxattr()*

openat()

mkdirat()

chmod()

lgetxattr()*

read()

fcntl()

fchmod()

fgetxattr()*

readv()

statvfs()

fchmodat()

listxattr()*

write()

fstatvfs()

chroot()

llistxattr()*

writev()

getvfsstat()

fsync()

flistxattr()*

stat()

pipe()

fdatasync()

removexattr()*

lstat()

futimens()

pread()

lremovexattr()*

fstat()

utimensat()

pwrite()

fremovexattr()*

fstatat()

link()

sendfile()

acl_set_file()*

lseek()

linkat()

getdents()

acl_get_file()*

close()

symlink()

sync()

acl_delete_def_file()*

rename()

symlinkat()

ioctl()

 

renameat()

unlinkat()

setxattr()*

 

Функции, реализуемые библиотекой vfs_net

read()

bind()

getsockname()

recvfrom()

readv()

listen()

gethostbyname()

recvmsg()

write()

connect()

getnetbyaddr()

send()

writev()

accept()

getnetbyname()

sendto()

fstat()

poll()

getnetent()

sendmsg()

close()

shutdown()

setnetent()

ioctl()

fcntl()

getnameinfo()

endnetent()

sysctl()

fstatvfs()

getaddrinfo()

getprotobyname()

 

pipe()

freeaddrinfo()

getprotobynumber()

 

futimens()

getifaddrs()

getsockopt()

 

socket()

freeifaddrs()

setsockopt()

 

socketpair()

getpeername()

recv()

 

Если в VFS нет реализации вызванной функции, возвращается код ошибки EIO.

В начало
[Topic vfs_overview]

Создание IPC-канала до VFS

В этом примере процесс Client использует файловые системы и сетевой стек, а процесс VfsFsnet обрабатывает IPC-запросы процесса Client, связанные с использованием файловых систем и сетевого стека. Такой подход используется в тех случаях, когда не требуется разделение информационных потоков, связанных с файловыми системами и сетевым стеком.

Имя IPC-канала должно задаваться макросом _VFS_CONNECTION_ID, определенным в заголовочном файле sysroot-*-kos/include/vfs/defs.h из состава KasperskyOS SDK.

Init-описание примера:

init.yaml

- name: Client connections: - target: VfsFsnet id: {var: _VFS_CONNECTION_ID, include: vfs/defs.h} - name: VfsFsnet
В начало
[Topic client_and_vfs_ipc_channel]

Включение функциональности VFS в программу

В этом примере программа Client включает функциональность программы VFS для работы с сетевым стеком (см. рис. ниже).

Библиотеки компонента VFS в составе программы

Выполняется компиляция файла реализации client.c и компоновка с библиотеками vfs_local, vfs_implementation и dnet_implementation:

CMakeLists.txt

project (client) include (platform/nk) # Установка флагов компиляции project_header_default ("STANDARD_GNU_17:YES" "STRICT_WARNINGS:NO") # Генерация файла Client.edl.h nk_build_edl_files (client_edl_files NK_MODULE "client" EDL "${CMAKE_SOURCE_DIR}/resources/edl/Client.edl") add_executable (Client "src/client.c") add_dependencies (Client client_edl_files) # Компоновка с библиотеками VFS target_link_libraries (Client ${vfs_LOCAL_LIB} ${vfs_IMPLEMENTATION_LIB} ${dnet_IMPLEMENTATION_LIB}

Если требуется, чтобы программа Client использовала файловые системы, то необходимо выполнить компоновку с библиотеками vfs_local и vfs_fs, а также с библиотеками реализации этих файловых систем. Кроме того, необходимо было бы добавить в решение драйвер блочного устройства.

В начало
[Topic client_and_vfs_linked]

Обзор: параметры запуска и переменные окружения VFS

Параметры запуска программы VFS

  • -l <запись в формате fstab>

    Параметр запуска -l монтирует заданную файловую систему.

  • -f <путь к файлу fstab>

    Параметр -f монтирует файловые системы, указанные в файле fstab. Если переменная окружения UNMAP_ROMFS не определена, то поиск файла fstab будет выполнен в ROMFS-образе. Если переменная окружения UNMAP_ROMFS определена, то поиск файла fstab будет выполнен в файловой системе, заданной через переменную окружения ROOTFS.

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

Переменные окружения программы VFS

  • UNMAP_ROMFS

    Если переменная окружения UNMAP_ROMFS определена, то ROMFS-образ будет удален из памяти. Это позволяет сэкономить память, а также при использовании параметра запуска -f дает возможность выполнить поиск файла fstab не в ROMFS-образе, а в файловой системе, заданной через переменную окружения ROOTFS.

    Пример использования переменной окружения UNMAP_ROMFS

  • ROOTFS = <запись в формате fstab>

    Переменная окружения ROOTFS позволяет монтировать заданную файловую систему в корневую директорию. При использовании параметра запуска -f комбинация переменных окружения ROOTFS и UNMAP_ROMFS дает возможность выполнить поиск файла fstab не в ROMFS-образе, а в файловой системе, заданной через переменную окружения ROOTFS.

    Пример использования переменной окружения ROOTFS

  • VFS_CLIENT_MAX_THREADS

    Переменная окружения VFS_CLIENT_MAX_THREADS позволяет переопределить параметр конфигурирования SDK VFS_CLIENT_MAX_THREADS.

  • VFS_NETWORK_BACKEND=<имя VFS-бэкенда>:<имя IPC-канала до процесса VFS>

    Переменная окружения VFS_NETWORK_BACKEND задает VFS-бэкенд для работы с сетевым стеком. Можно указать имя стандартного VFS-бэкенда: client (для программы, исполняющейся в контексте клиентского процесса), server (для программы VFS, исполняющейся в контексте серверного процесса) или local, а также имя пользовательского VFS-бэкенда. Если используется VFS-бэкенд local, то имя IPC-канала не указывается (VFS_NETWORK_BACKEND=local:). Может быть указано более одного IPC-канала через запятую.

  • VFS_FILESYSTEM_BACKEND=<имя VFS-бэкенда>:<имя IPC-канала до процесса VFS>

    Переменная окружения VFS_FILESYSTEM_BACKEND задает VFS-бэкенд для работы с файловыми системами. Имя VFS-бэкенда и имя IPC-канала до процесса VFS задаются так же, как и в переменной окружения VFS_NETWORK_BACKEND.

Значения по умолчания для параметров запуска и переменных окружения VFS

Для исполняемого файла VfsRamFs:

ROOTFS = ramdisk0,0 / ext4 0 VFS_FILESYSTEM_BACKEND = server:kl.VfsRamFs

Для исполняемого файла VfsSdCardFs:

ROOTFS = mmc0,0 / fat32 0 VFS_FILESYSTEM_BACKEND = server:kl.VfsSdCardFs -l nodev /tmp ramfs 0 -l nodev /var ramfs 0

Для исполняемого файла VfsNet:

VFS_NETWORK_BACKEND = server:kl.VfsNet VFS_FILESYSTEM_BACKEND = server:kl.VfsNet -l devfs /dev devfs 0
В начало
[Topic vfs_args_and_envs_overview]

Монтирование файловых систем при запуске VFS

При запуске программ VfsRamFs и VfsSdCardFs по умолчанию монтируется только файловая система RAMFS в корневую директорию. Если требуется монтировать другие файловые системы, это можно сделать не только с помощью вызова функции mount(), но и установив параметры запуска и переменные окружения программы VFS.

Файловые системы ROMFS и squashfs предназначены только для чтения, поэтому для монтирования этих файловых систем нужно указать параметр ro.

Использование параметра запуска -l

Одним из способов монтировать файловую систему является установка для программы VFS параметра запуска -l <запись в формате fstab>.

В этих примерах при запуске программы VFS будут монтированы файловые системы devfs и ROMFS:

init.yaml.(in)

... - name: VfsFirst args: - -l - devfs /dev devfs 0 - -l - romfs /etc romfs ro ...

CMakeLists.txt

... set_target_properties (${vfs_ENTITY} PROPERTIES EXTRA_ARGS " - -l - devfs /dev devfs 0 - -l - romfs /etc romfs ro") ...

Использование файла fstab из ROMFS-образа

При сборке решения можно добавить файл fstab в ROMFS-образ. Этот файл можно использовать для монтирования файловых систем, установив для программы VFS параметр запуска -f <путь к файлу fstab>.

В этих примерах при запуске программы VFS будут монтированы файловые системы, заданные через файл fstab, который был добавлен при сборке решения в ROMFS-образ:

init.yaml.(in)

... - name: VfsSecond args: - -f - fstab ...

CMakeLists.txt

... set_target_properties (${vfs_ENTITY} PROPERTIES EXTRA_ARGS " - -f - fstab") ...

Использование "внешнего" файла fstab

Если файл fstab находится не в ROMFS-образе, а в другой файловой системе, то для использования этого файла необходимо установить для программы VFS следующие параметры запуска и переменные окружения:

  1. ROOTFS. Эта переменная окружения позволяет монтировать в корневую директорию файловую систему, содержащую файл fstab.
  2. UNMAP_ROMFS. Если эта переменная окружения определена, то поиск файла fstab будет выполнен в файловой системе, заданной через переменную окружения ROOTFS.
  3. -f. Этот параметр запуска используется, чтобы монтировать файловые системы, указанные в файле fstab.

В этих примерах при запуске программы VFS в корневую директорию будет монтирована файловая система ext2, в которой должен находиться файл fstab по пути /etc/fstab:

init.yaml.(in)

... - name: VfsThird args: - -f - /etc/fstab env: ROOTFS: ramdisk0,0 / ext2 0 UNMAP_ROMFS: 1 ...

CMakeLists.txt

... set_target_properties (${vfs_ENTITY} PROPERTIES EXTRA_ARGS " - -f - /etc/fstab" EXTRA_ENV " ROOTFS: ramdisk0,0 / ext2 0 UNMAP_ROMFS: 1") ...
В начало
[Topic mount_on_start]

Разделение информационных потоков с помощью VFS-бэкендов

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

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

Схема взаимодействия процессов

Init-описание примера:

init.yaml

entities: - name: Client connections: - target: VfsFirst id: VFS1 - target: VfsSecond id: VFS2 env: VFS_FILESYSTEM_BACKEND: client:VFS1 VFS_NETWORK_BACKEND: client:VFS2 - name: VfsFirst env: VFS_FILESYSTEM_BACKEND: server:VFS1 - name: VfsSecond env: VFS_NETWORK_BACKEND: server:VFS2
В начало
[Topic client_and_two_vfs]

Создание VFS-бэкенда

В этом примере показано, как создать и использовать собственный VFS-бэкенд.

Процесс Client использует файловые системы fat32 и ext4. Процесс VfsFirst обеспечивает работу с файловой системой fat32, а процесс VfsSecond дает возможность работать с файловой системой ext4. Через переменные окружения программ, исполняющихся в контекстах процессов Client, VfsFirst и VfsSecond, заданы VFS-бэкенды, которые обеспечивают обработку IPC-запросов процесса Client процессом VfsFirst или VfsSecond в зависимости от того, какую файловую систему использует процесс Client. В результате этого IPC-запросы процесса Client, связанные с использованием файловой системы fat32, обрабатываются процессом VfsFirst, а IPC-запросы процесса Client, связанные с использованием файловой системы ext4, обрабатываются процессом VfsSecond (см. рис. ниже).

На стороне процесса VfsFirst файловая система fat32 монтируется в директорию /mnt1. На стороне процесса VfsSecond файловая система ext4 монтируется в директорию /mnt2. Пользовательский VFS-бэкенд custom_client, используемый на стороне процесса Client, позволяет отправлять IPC-запросы по IPC-каналу VFS1 или VFS2 в зависимости от того, начинается ли путь к файлу с /mnt1 или нет. При этом пользовательский VFS-бэкенд использует в качестве посредника стандартный VFS-бэкенд client.

Схема взаимодействия процессов

Исходный код VFS-бэкенда

Этот файл реализации содержит исходный код VFS-бэкенда custom_client, использующего стандартные VFS-бэкенды client:

backend.c

#include <vfs/vfs.h> #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <errno.h> #include <string.h> #include <getopt.h> #include <assert.h> /* Код управления файловыми дескрипторами */ #define MAX_FDS 50 struct entry { Handle handle; bool is_vfat; }; struct fd_array { struct entry entries[MAX_FDS]; int pos; pthread_rwlock_t lock; }; struct fd_array fds = { .pos = 0, .lock = PTHREAD_RWLOCK_INITIALIZER }; int insert_entry(Handle fd, bool is_vfat) { pthread_rwlock_wrlock(&fds.lock); if (fds.pos == MAX_FDS) { pthread_rwlock_unlock(&fds.lock); return -1; } fds.entries[fds.pos].handle = fd; fds.entries[fds.pos].is_vfat = is_vfat; fds.pos++; pthread_rwlock_unlock(&fds.lock); return 0; } struct entry *find_entry(Handle fd) { pthread_rwlock_rdlock(&fds.lock); for (int i = 0; i < fds.pos; i++) { if (fds.entries[i].handle == fd) { pthread_rwlock_unlock(&fds.lock); return &fds.entries[i]; } } pthread_rwlock_unlock(&fds.lock); return NULL; } /* Структура пользовательского VFS-бэкенда */ struct context { struct vfs wrapper; pthread_rwlock_t lock; struct vfs *vfs_vfat; struct vfs *vfs_ext4; }; struct context ctx = { .wrapper = { .dtor = _vfs_backend_dtor, .disconnect_all_clients = _disconnect_all_clients, .getstdin = _getstdin, .getstdout = _getstdout, .getstderr = _getstderr, .open = _open, .read = _read, .write = _write, .close = _close, } }; /* Реализация методов пользовательского VFS-бэкенда */ static bool is_vfs_vfat_path(const char *path) { char vfat_path[5] = "/mnt1"; if (memcmp(vfat_path, path, sizeof(vfat_path)) != 0) return false; return true; } static void _vfs_backend_dtor(struct vfs *vfs) { ctx.vfs_vfat->dtor(ctx.vfs_vfat); ctx.vfs_ext4->dtor(ctx.vfs_ext4); } static void _disconnect_all_clients(struct vfs *self, int *error) { (void)self; (void)error; ctx.vfs_vfat->disconnect_all_clients(ctx.vfs_vfat, error); ctx.vfs_ext4->disconnect_all_clients(ctx.vfs_ext4, error); } static Handle _getstdin(struct vfs *self, int *error) { (void)self; Handle handle = ctx.vfs_vfat->getstdin(ctx.vfs_vfat, error); if (handle != INVALID_HANDLE) { if (insert_entry(handle, true)) { *error = ENOMEM; return INVALID_HANDLE; } } return handle; } static Handle _getstdout(struct vfs *self, int *error) { (void)self; Handle handle = ctx.vfs_vfat->getstdout(ctx.vfs_vfat, error); if (handle != INVALID_HANDLE) { if (insert_entry(handle, true)) { *error = ENOMEM; return INVALID_HANDLE; } } return handle; } static Handle _getstderr(struct vfs *self, int *error) { (void)self; Handle handle = ctx.vfs_vfat->getstderr(ctx.vfs_vfat, error); if (handle != INVALID_HANDLE) { if (insert_entry(handle, true)) { *error = ENOMEM; return INVALID_HANDLE; } } return handle; } static Handle _open(struct vfs *self, const char *path, int oflag, mode_t mode, int *error) { (void)self; Handle handle; bool is_vfat = false; if (is_vfs_vfat_path(path)) { handle = ctx.vfs_vfat->open(ctx.vfs_vfat, path, oflag, mode, error); is_vfat = true; } else handle = ctx.vfs_ext4->open(ctx.vfs_ext4, path, oflag, mode, error); if (handle == INVALID_HANDLE) return INVALID_HANDLE; if (insert_entry(handle, is_vfat)) { if (is_vfat) ctx.vfs_vfat->close(ctx.vfs_vfat, handle, error); *error = ENOMEM; return INVALID_HANDLE; } return handle; } static ssize_t _read(struct vfs *self, Handle fd, void *buf, size_t count, bool *nodata, int *error) { (void)self; struct entry *found_entry = find_entry(fd); if (found_entry != NULL && found_entry->is_vfat) return ctx.vfs_vfat->read(ctx.vfs_vfat, fd, buf, count, nodata, error); return ctx.vfs_ext4->read(ctx.vfs_ext4, fd, buf, count, nodata, error); } static ssize_t _write(struct vfs *self, Handle fd, const void *buf, size_t count, int *error) { (void)self; struct entry *found_entry = find_entry(fd); if (found_entry != NULL && found_entry->is_vfat) return ctx.vfs_vfat->write(ctx.vfs_vfat, fd, buf, count, error); return ctx.vfs_ext4->write(ctx.vfs_ext4, fd, buf, count, error); } static int _close(struct vfs *self, Handle fd, int *error) { (void)self; struct entry *found_entry = find_entry(fd); if (found_entry != NULL && found_entry->is_vfat) return ctx.vfs_vfat->close(ctx.vfs_vfat, fd, error); return ctx.vfs_ext4->close(ctx.vfs_ext4, fd, error); } /* Конструктор пользовательского VFS-бэкенда. ctx.vfs_vfat и ctx.vfs_ext4 инициализируются * как стандартные бэкенды с именем "client". */ static struct vfs *_vfs_backend_create(Handle client_id, const char *config, int *error) { (void)config; ctx.vfs_vfat = _vfs_init("client", client_id, "VFS1", error); assert(ctx.vfs_vfat != NULL && "Can't initialize client backend!"); assert(ctx.vfs_vfat->dtor != NULL && "VFS FS backend has not set the destructor!"); ctx.vfs_ext4 = _vfs_init("client", client_id, "VFS2", error); assert(ctx.vfs_ext4 != NULL && "Can't initialize client backend!"); assert(ctx.vfs_ext4->dtor != NULL && "VFS FS backend has not set the destructor!"); return &ctx.wrapper; } /* Регистрация пользовательского VFS-бэкенда под именем custom_client */ static void _vfs_backend(create_vfs_backend_t *ctor, const char **name) { *ctor = &_vfs_backend_create; *name = "custom_client"; } REGISTER_VFS_BACKEND(_vfs_backend)

Компоновка программы Client

Создание статической библиотеки VFS-бэкенда:

CMakeLists.txt

... add_library (backend_client STATIC "src/backend.c") ...

Компоновка программы Client со статической библиотеки VFS-бэкенда:

CMakeLists.txt

... add_dependencies (Client vfs_backend_client backend_client) target_link_libraries (Client pthread ${vfs_CLIENT_LIB} "-Wl,--whole-archive" backend_client "-Wl,--no-whole-archive" backend_client ) ...

Установка параметров запуска и переменных окружения программ

Init-описание примера:

init.yaml

entities: - name: vfs_backend.Client connections: - target: vfs_backend.VfsFirst id: VFS1 - target: vfs_backend.VfsSecond id: VFS2 env: _VFS_FILESYSTEM_BACKEND: custom_client:VFS1,VFS2 - name: vfs_backend.VfsFirst args: - -l - ahci0 /mnt1 fat32 0 env: _VFS_FILESYSTEM_BACKEND: server:VFS1 - name: vfs_backend.VfsSecond - -l - ahci1 /mnt2 ext4 0 env: _VFS_FILESYSTEM_BACKEND: server:VFS2
В начало
[Topic vfs_backends]

Динамическая настройка сетевого стека

Чтобы изменить параметры сетевого стека, заданные по умолчанию, нужно использовать функцию sysctl() или sysctlbyname(), объявленные в заголовочном файле sysroot-*-kos/include/sys/sysctl.h из состава KasperskyOS SDK. Параметры, которые можно изменить, приведены в таблице ниже.

Настраиваемые параметры сетевого стека

Название параметра

Описание параметра

net.inet.ip.ttl

Максимальное время жизни (англ. Time To Live, TTL) отправляемых IP-пакетов. Не влияет на протокол ICMP.

net.inet.ip.mtudisc

Если имеет значение 1, то задействован режим "Path MTU Discovery" (RFC 1191), влияющий на максимальный размер TCP-сегмента (англ. Maximum Segment Size, MSS). В этом режиме значение MSS определяется ограничениями узлов сети. Если режим "Path MTU Discovery" не задействован, то значение MSS не превышает заданного параметром net.inet.tcp.mssdflt.

net.inet.tcp.mssdflt

Значение MSS (в байтах), которое применяется, если только взаимодействующая сторона не сообщила это значение при открытии TCP-соединения, или не задействован режим "Path MTU Discovery" (RFC 1191). Также это значение MSS передается взаимодействующей стороне при открытии TCP-соединения.

net.inet.tcp.minmss

Минимальное значение MSS, в байтах.

net.inet.tcp.mss_ifmtu

Если имеет значение 1, то значение MSS при открытии TCP-соединения рассчитывается, исходя из максимального размера блока передаваемых данных (англ. Maximum Transmission Unit, MTU) задействованного сетевого интерфейса. Если имеет значение 0, то значение MSS при открытии TCP-соединения рассчитывается, исходя из MTU того сетевого интерфейса, который имеет наибольшее значение этого параметра среди всех имеющихся сетевых интерфейсов (кроме loopback-интерфейса).

net.inet.tcp.keepcnt

Число повторных отправок проверочных сообщений (англ. Keep-Alive Probes, KA) без ответа, после выполнения которых TCP-соединение считается закрытым. Если имеет значение 0, то число отправок KA не ограничено.

net.inet.tcp.keepidle

Время неактивности TCP-соединения, по истечении которого начинают отправляться KA. Задается в условных единицах, которые можно перевести в секунды, разделив на значение параметра net.inet.tcp.slowhz.

net.inet.tcp.keepintvl

Время между повторными отправками KA при отсутствии ответа. Задается в условных единицах, которые можно перевести в секунды, разделив на значение параметра net.inet.tcp.slowhz.

net.inet.tcp.recvspace

Размер буфера для принимаемых по протоколу TCP данных, в байтах.

net.inet.tcp.sendspace

Размер буфера для отправляемых по протоколу TCP данных, в байтах.

net.inet.udp.recvspace

Размер буфера для принимаемых по протоколу UDP данных, в байтах.

net.inet.udp.sendspace

Размер буфера для отправляемых по протоколу UDP данных, в байтах.

Пример настройки MSS:

static const int mss_max = 1460; static const int mss_min = 100; static const char* mss_max_opt_name = "net.inet.tcp.mssdflt"; static const char* mss_min_opt_name = "net.inet.tcp.minmss"; int main(void) { ... if ((sysctlbyname(mss_max_opt_name, NULL, NULL, &mss_max, sizeof(mss_max)) != 0) || (sysctlbyname(mss_min_opt_name, NULL, NULL, &mss_min, sizeof(mss_min)) != 0)) { ERROR(START, "Can't set tcp default maximum/minimum MSS value."); return EXIT_FAILURE; } }
В начало
[Topic vfs_net_stack_dyn_conf][Topic sc_using_ipc]

Создание IPC-каналов

IPC-каналы могут быть созданы статически и динамически.

Статическое создание IPC-каналов

Статическое создание IPC-канала имеет следующие особенности:

  • Создание IPC-канала выполняется родительским процессом, запускающим клиента и сервера (обычно это Einit).
  • Клиент и сервер еще не запущены в момент создания IPC-канала.
  • Нельзя создать новый IPC-канал вместо удаленного.

Статически создаются IPC-каналы, заданные в init-описании. Также для статического создания IPC-каналов можно использовать API task.h.

Чтобы получить клиентский и серверный IPC-дескрипторы и идентификатор службы (RIID), нужно использовать API sl_api.h.

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

Динамическое создание IPC-каналов позволяет изменять топологию взаимодействия процессов "на лету". Например, это требуется, если неизвестно, какой именно сервер предоставляет службу, необходимую клиенту.

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

  • Создание IPC-канала выполняется совместно клиентом и сервером.
  • Клиент и сервер уже запущены в момент создания IPC-канала.
  • Можно создать новый IPC-канал вместо удаленного.

Для динамического создания IPC-каналов нужно использовать системную программу DCM.

Помимо использования системной программы DCM существует другой способ динамического создания IPC-каналов, который заключается в использовании API cm_api.h и ns_api.h, предоставляемых библиотекой libkos совместно с системной программой NameServer. Этот способ является устаревшим, поэтому его использование не рекомендуется.

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

В начало
[Topic ipc_channels]

Добавление в решение службы из состава KasperskyOS Community Edition

Чтобы программа Client могла использовать ту или иную функциональность через механизм IPC, необходимо:

  1. Найти в составе KasperskyOS Community Edition исполняемый файл (условно назовем его Server), реализующий нужную функциональность. (Под функциональностью мы здесь понимаем одну или несколько служб, имеющих самостоятельные IPC-интерфейсы)
  2. Подключить CMake-пакет, содержащий файл Server и его клиентскую библиотеку.
  3. Добавить исполняемый файл Server в образ решения.
  4. Изменить init-описание так, чтобы при старте решения программа Einit запускала новый серверный процесс из исполняемого файла Server и соединяла его IPC-каналом с процессом, запускаемым из файла Client.

    Необходимо указать корректное имя IPC-канала, чтобы транспортные библиотеки могли идентифицировать этот канал и найти его IPC-дескрипторы. Корректное имя IPC-канала, как правило, совпадает с именем класса серверного процесса. VFS при этом является исключением.

  5. Изменить PSL-описание так, чтобы разрешить запуск серверного процесса и IPC-взаимодействие между клиентом и сервером.
  6. Подключить в исходном коде программы Client заголовочный файл с методами сервера.
  7. Скомпоновать программу Client с клиентской библиотекой.

Пример добавления GPIO-драйвера в решение

В составе KasperskyOS Community Edition есть файл gpio_hw, реализующий функциональность GPIO-драйвера.

Следующие команды подключают CMake‑пакет gpio:

.\CMakeLists.txt

... find_package (gpio REQUIRED COMPONENTS CLIENT_LIB ENTITY) include_directories (${gpio_INCLUDE}) ...

Добавление исполняемого файла gpio_hw в образ решения производится с помощью переменной gpio_HW_ENTITY, имя которой можно найти в конфигурационном файле пакета – /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/lib/cmake/gpio/gpio-config.cmake:

einit\CMakeLists.txt

... set (ENTITIES Client ${gpio_HW_ENTITY}) ...

В init-описание нужно добавить следующие строки:

init.yaml.in

... - name: client.Client connections: - target: kl.drivers.GPIO id: kl.drivers.GPIO - name: kl.drivers.GPIO path: gpio_hw

В PSL-описание нужно добавить следующие строки:

security.psl.in

... execute src=Einit, dst=kl.drivers.GPIO { grant() } request src=client.Client, dst=kl.drivers.GPIO { grant() } response src=kl.drivers.GPIO, dst=client.Client { grant() } ...

В коде программы Client нужно подключить заголовочный файл, в котором объявлены методы GPIO-драйвера:

client.c

... #include <gpio/gpio.h> ...

Наконец, нужно скомпоновать программу Client с клиентской библиотекой GPIO:

client\CMakeLists.txt

... target_link_libraries (Client ${gpio_CLIENT_LIB}) ...

Для корректной работы GPIO‑драйвера может понадобиться добавить в решение компонент BSP. Чтобы не усложнять этот пример, мы не рассматриваем здесь BSP. Подробнее см. пример gpio_output: /opt/KasperskyOS-Community-Edition-<version>/examples/gpio_output

В начало
[Topic using_sdk_endpoints_examples][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]

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

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

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

Server.edl

task class 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, имена которых начинаются с символа _ или __, поскольку эти функция являются деталями внутренней реализации, которые могут изменяться.

На аппаратных платформах с процессорной архитектурой Arm входные и выходные параметры API нельзя сохранять в памяти типа "Device memory", поскольку это может привести к неопределенному поведению. Параметры API нужно сохранять в памяти типа "Normal memory". Чтобы копировать данные из памяти типа "Device memory" в память типа "Normal memory" и обратно, нужно использовать функцию RtlPedanticMemcpy(), объявленную в заголовочном файле sysroot-*-kos/include/rtl/string_pedantic.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() или NkKosDoDispatchEx(), объявленных в заголовочном файле 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_aligned();
  • макрос nk_arena_alloc();
  • макрос nk_arena_store_aligned();
  • макрос nk_arena_store();
  • функцию NkKosCopyStringToArena().

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

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

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

Зарезервированный участок арены можно уменьшить. Для этого нужно использовать макрос 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() не требуется.)

Отменять текущее резервирование участков арены не требуется, если для получения IPC-сообщения непосредственно используется одна из функций API syscalls.h, но перед вызовами методов транспортного кода на стороне клиента и функции nk_transport_recv() на стороне сервера это делать необходимо. (Функция nk_transport_recv() объявлена в заголовочном файле sysroot-*-kos/include/nk/transport.h из состава KasperskyOS SDK.)

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

Чтобы получить указатели на участки арены и число объектов, вмещающихся в этих участках, нужно использовать макрос 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().

Сведения о функциях и макросах 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_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_allocated_size()

Назначение

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

Параметры

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

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

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

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

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

nk_arena_init_final()

Назначение

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

Параметры

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

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

Нет.

nk_arena_alloc_aligned()

Назначение

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

Параметры

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

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

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

nk_arena_alloc()

Назначение

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

Параметры

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

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

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

nk_arena_store_aligned()

Назначение

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

Параметры

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

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

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

nk_arena_store()

Назначение

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

Параметры

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

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

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

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.

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

В начало
[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

Тип C++

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 (определен в sysroot-*-kos/include/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. Подключить заголовочные файлы из состава KasperskyOS SDK:
    • sysroot-*-kos/include/kosipc/application.h
    • sysroot-*-kos/include/kosipc/api.h
    • 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().

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

  1. Подключить сгенерированный заголовочный файл *.edl.cpp.h, содержащий описание компонентной структуры программы, включая все предоставляемые службы.
  2. Подключить заголовочные файлы из состава KasperskyOS SDK:
    • sysroot-*-kos/include/kosipc/event_loop.h
    • sysroot-*-kos/include/kosipc/api.h
    • 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.
В начало
[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. Связать объект класса 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-канала.

    Вызвать функцию ServeDynamicChannel() (см. на шаг 7) нужно строго до создания объекта типа kosipc::SimpleConnectionAcceptor.

  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.endpoint1 = &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]

Работа с KPA-пакетами

KPA-пакет представляет собой файл в проприетарном формате KPA, который служит упаковкой для программы, предназначенной для установки в решение на базе KasperskyOS. Он включает в себя следующие элементы:

  • Заголовок KPA-пакета. Представляет собой уникальную последовательность байтов, которая используется для идентификации формата KPA.
  • Манифест KPA-пакета. Представляет собой структуру данных, описывающую файл формата JSON, который содержит подробную информацию о KPA-пакете.
  • Компоненты KPA-пакета. Представляют собой выровненные байтовые последовательности с произвольным содержимым. В качестве компонентов KPA-пакета могут выступать исполняемые файлы, библиотеки, текстовые данные, а также любые другие данные, которые требуются для работы программы.
  • Индекс KPA-пакета. Представляет собой структуру данных, которая описывает количество компонентов KPA-пакета, их хеш-суммы и размеры.

KPA-пакет в сжатом формате представляет собой файл в формате KPAC. Формат KPAC представляет собой вариант формата ZIP, который содержит в себе следующие ограничения: отсутствие иерархии директорий, отсутствие вложенных zip-архивов, ограничение на размер и количество входящих файлов. Файл формата KPAC содержит в себе KPA-пакет, его внешнюю подпись и индексный файл. Внешняя подпись KPA-пакета представляет собой файл проприетарного формата KCAT и располагается вне файла KPA-пакета. Внешняя подпись защищает от подмены и внесения изменений как сам KPA-пакет, так и индексный файл KPA-пакета. Индексный файл KPA-пакета представляет собой файл проприетарного формата KIDX и используется для проверки целостности KPA-пакета.

Управление KPA-пакетами

Для управления KPA-пакетами в составе KasperskyOS Community Edition поставляются:

  • CMake-библиотека platform/kpa, предназначенная для сборки KPA-пакетов в процессе сборки решения на базе KasperskyOS. При использовании функций CMake-библиотеки platform/kpa манифест KPA-пакета создается автоматически.
  • Утилиты, которые позволяют собрать в системе, где установлен SDK, KPA-пакет из исходных файлов программы и установить KPA-пакет в собираемый образ решения на базе KasperskyOS.
  • Компонент PackageManager, который позволяет установить KPA-пакеты в работающее решение на базе KasperskyOS, а также удалить KPA-пакеты и получить сведения о них.

В этом разделе

Манифест KPA-пакета

Утилиты для управления KPA-пакетами

Сценарий использования компонента PackageManager

В начало
[Topic kpa_package]

Манифест KPA-пакета

Манифест KPA-пакета представляет собой файл формата JSON и содержит информацию, которая необходима при установке и использовании KPA-пакета. Список основных ключей манифеста KPA-пакета указан в таблице ниже.

Основные ключи манифеста KPA-пакета

Имя ключа

Тип значения

Описание

Обязательный

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

version

Строка

Номер версии манифеста в виде {major}.{minor}, где major – мажорная версия манифеста, minor – минорная версия манифеста. Текущая версия манифеста – 2.0. Не рекомендуется изменять эту версию, так как изменение может повлечь проблемы с работоспособностью программы.

Да

"version": "2.0"

digestType

Строка

Идентификатор (OID) хеш-функции, которая используется для вычисления хеш-суммы объектных файлов и других хеш-сумм. Если значение не задано или является пустой строкой, то по умолчанию используется значение "2.16.840.1.101.3.4.2.1" (хеш-функция SHA256).

Нет

"digestType": "2.16.840.1.101.3.4.2.1"

application

Объект

Информация о программе.

Да

Объект application

platform

Объект

Информация о платформе, для которой поставляется программа.

Да

Объект platform

components

Список объектов

Список компонентов KPA-пакета.

Нет

Список объектов components

runConfiguration

Список объектов

Список конфигураций запуска программы.

Нет

Список объектов runConfiguration

privateStorage

Объект

Изолированное хранилище данных программы.

Нет

Объект privateStorage

extensions

Объект

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

Нет

Произвольное содержимое

В этом разделе

Объект application

Объект platform

Список объектов components

Список объектов runConfiguration

Объект privateStorage

В начало
[Topic kpa_manifest]

Объект application

Объект application включает в себя ключи, содержащие информацию о программе, устанавливаемой из KPA-пакета. Список этих ключей указан в таблице ниже.

Список ключей объекта application

Имя ключа

Тип значения

Описание

Обязательный

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

id

Строка

Уникальный идентификатор программы. Этот идентификатор позволяет однозначно идентифицировать устанавливаемую программу. После публикации идентификатор программы меняться не должен. Если идентификатор программы изменяется, то все остальные участники жизненного цикла установленной программы будут рассматривать её как другую программу.

Да

"id": "helloworld"

name

Строка

Отображаемое название программы (используется, например, для отображения в пользовательских интерфейсах).

Да

"name": "Hello World"

version

Строка

Версия программы.

Да

"version": "1.2.34"

buildNumber

Строка

Номер сборки программы.

Да

"buildNumber": "182"

systemApplication

Логический

Логическое значение, указывающее на то, является ли программа системной: true – системная, false – прикладная. В текущей версии KasperskyOS Community Edition единственное доступное значение – false.

Да

"systemApplication": false

description

Строка

Описание программы.

Нет

"description": "Sample application"

В начало
[Topic manifest_application]

Объект platform

Объект platform включает в себя ключи, содержащие информацию о платформе, для которой программа поставляется. Список этих ключей указан в таблице ниже.

Список ключей объекта platform

Имя ключа

Тип значения

Описание

Обязательный

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

id

Строка

Идентификатор операционной системы.

Да

"id": "kos.ce"

sdk

Объект

Целевой уровень API, который должно предоставлять устройство с KasperskyOS Community Edition для наилучшего функционирования программы, а также название и версия SDK, которым собрана эта программа.

Да

"sdk":

{

"buildSdkName": "KasperskyOS-Community-Edition-RaspberryPi4b"

"buildSdkVersion": "1.3.0.3"

"targetApiLevel": 4

}

 

buildSdkName

Строка

Название SDK, которым собрана эта программа.

Да

buildSdkVersion

Строка

Версия SDK, которым собрана эта программа.

Да

targetApiLevel

Целое число

Целевой уровень API, который должно предоставлять устройство с KasperskyOS Community Edition.

Да

hwSpecification

Объект

Техническая спецификация аппаратной платформы.

Да

"hwSpecification":

{

"arch": "aarch64-kos"

"cpu": "bcm2711"

}

 

arch

Строка

Архитектура аппаратной платформы.

Да

cpu

Строка

Название процессора, на котором работает аппаратная платформа.

Да

В начало
[Topic manifest_platform]

Список объектов components

Список объектов components включает в себя ключи, содержащие информацию о компонентах, добавленных в KPA-пакет. Список этих ключей указан в таблице ниже.

Список ключей для описания экземпляра компонента в списке объектов components

Имя ключа

Тип значения

Описание

Обязательный

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

name

Строка

Имя компонента KPA-пакета.

Да

"name": "imageHighResolution"

directoryPath

Строка

Путь к директории относительно пути /<имя пакета>/res, в которую должен быть установлен компонент KPA-пакета. Игнорируется, если в значении ключа componentType указаны значения bin или lib.

Нет

"directoryPath": "images/highResolution"

digest

Строка

Хеш-сумма файла компонента KPA-пакета.

Да

"digest": "2d541fe063c195a3b8a90204f2c234e1b5daf664db381faa4f2b81067733d6c3"

componentType

Строка

Тип компонента:

  • bin – исполняемый файл. При установке программы помещается в директорию /<имя_программы>/bin.
  • lib – разделяемая библиотека. При установке программы помещается в директорию /<имя_программы>/lib.
  • res – произвольный ресурс. Если задано значение ключа directoryPath, то при установке программы ресурс помещается в директорию /<имя_пакета>/res/<directoryPath>, иначе ресурс помещается в директорию /<имя_пакета>/res.
  • manifestLocale – файл, содержащий информацию для локализации манифеста KPA-пакета. Файлы локализации манифеста KPA-пакета при установке программы помещаются в директорию /<имя_программы>/manifest_locales, если не задано значение для ключа directoryPath.

Да

"componentType": "res"

В начало
[Topic manifest_components]

Список объектов runConfiguration

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

Список ключей для описания экземпляра конфигурации запуска списка объектов runConfiguration

Имя ключа

Тип значения

Описание

Обязательный

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

id

Строка

Уникальный в рамках данного KPA-пакета идентификатор конфигурации запуска программы.

Да

"id": "app"

name

Строка

Имя конфигурации запуска.

Является локализованной строкой.

Да

"name": "application"

или

"name": "@runConfigurationLocalizedName@"

type

Строка

Тип конфигурации запуска:

  • gui – процесс с графическим пользовательским интерфейсом;
  • service – процесс-служба.

Да

"type": "service"

args

Список строк

Список аргументов в виде массива строк.

Нет

"args":

[

"networkSpeed=4096",

"nthreads=2"

]

envVariables

Список объектов

Список переменных окружения.

Нет

"envVariables":

{

"name": "IMAGES"

"value": "images"

}

 

name

Строка

Имя переменной окружения.

Да (если используется envVariables)

value

Строка

Значение переменной окружения (может быть пустой строкой).

Да (если используется envVariables)

primary

Логический

Указывает, является эта конфигурация запуска первичной при старте программы: true – является первичной, false – не является.

Да

"primary": true

autorun

Логический

Указывает, является эта конфигурация запускаемой автоматически: true – является запускаемой автоматически, false – не является. Значение по умолчанию: false.

Нет

"autorun": false

eiid

Строка

Класс безопасности программы. Требуется для модуля безопасности KasperskyOS.

Нет

"eiid": "application.Application"

path

Строка

Путь к файлу компонента KPA-пакета. Путь задается относительно /<application_id>.

Да

"path": "bin/application"

usesService

Список объектов

Список конфигураций запуска, от которых зависит эта конфигурация запуска. Не поддерживается в текущей версии KasperskyOS Community Edition.

Нет

"usesService":

{

"name": "database"

"useType": "child"

}

 

name

Строка

Имя используемой конфигурации запуска. Если конфигурация запуска находится в другом KPA-пакете, то значение этого поля должно иметь формат "<имя_пакета>:<имя_конфигурации_запуска>". Если конфигурация запуска находится в этом KPA-пакете, то поле может иметь значение в формате "<имя_пакета>:<имя_конфигурации_запуска>" (<имя_пакета> соответствует текущему пакету) или только "<имя_конфигурации_запуска>".

Да (если используется usesService)

useType

Строка

Тип использования конфигурации запуска:

  • child – программа может запускать указанную конфигурацию запуска как дочерний процесс.
  • serviceCanUse – указанная служба может потребоваться программе.
  • serviceActive – указанная служба должна быть активна в момент запуска программы.

Да (если используется usesService)

В начало
[Topic manifest_run_configuration]

Объект privateStorage

Объект privateStorage включает в себя ключи, содержащие информацию о хранилище данных программы. Список этих ключей указан в таблице ниже.

Объект privateStorage не поддерживается в текущей версии KasperskyOS Community Edition.

Список ключей объекта privateStorage

Имя ключа

Тип значения

Описание

Обязательный

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

size

Строка

Размер хранилища данных программы в МБ.

Да

"size": "512"

fsType

Строка

Тип файловой системы хранилища данных программы.

Да

"fsType": "ext4"

В начало
[Topic manifest_private_storage]

Утилиты для управления KPA-пакетами

В составе KasperskyOS Community Edition поставляются утилиты для управления KPA-пакетами:

  • cas-pack для сборки KPA-пакета в системе, где установлен SDK;
  • cas-inspect для получения информации о содержимом KPA-пакета при работе с SDK;
  • cas-pm для установки одного или нескольких KPA-пакетов в собираемый образ решения на базе KasperskyOS.

Исполняемые файлы этих утилит расположены в директории /opt/KasperskyOS-Community-Edition-<version>/toolchain/bin/.

В этом разделе

Утилита cas-pack

Утилита cas-inspect

Утилита cas-pm

В начало
[Topic cas_tools]

Утилита cas-pack

В составе KasperskyOS Community Edition поставляется утилита cas-pack (исполняемый файл toolchain/bin/cas-pack) предназначенная для сборки KPA-пакета в системе, где установлен KasperskyOS Community Edition SDK.

Синтаксис shell-команды для запуска утилиты cas-pack:

cas-pack {-o|--output} <FILE> --manifest <FILE> --verify [--version] [-h|--help] <FILES>...

Параметры:

  • {-o|--output} <FILE>

    Полное имя файла собираемого KPA-пакета.

  • --manifest <FILE>

    Полное имя файла манифеста KPA-пакета.

  • <FILES>

    Список полных имен файлов, которые будут включены в KPA-пакет. Элементы списка разделяйте знаком пробела. Для выбора всех файлов в директории можно использовать знак * .

  • --verify

    Проверка наличия всех компонентов KPA-пакета, указанных в его манифесте, и отсутствия неуказанных компонентов, а также расчет контрольных сумм компонентов KPA-пакета и сравнение их с указанными в манифесте KPA-пакета.

  • --version

    Версия утилиты.

  • -h|--help

    Текст справки.

Пример shell-команды для запуска утилиты cas-pack:

# Упаковывает все исходные файлы программы, находящиеся в директории # files, в KPA-пакет с именем helloworld.kpa. При сборке KPA-пакета # используется информация из файла manifest.json, представляющего собой # манифест KPA-пакета. Собранный KPA-пакет верифицируется. /opt/KasperskyOS-Community-Edition-<version>/toolchain/bin/cas-pack --output ./helloworld.kpa --manifest ./manifest.json --verify ./files/*
В начало
[Topic tools_cas_pack]

Утилита cas-inspect

В составе KasperskyOS Community Edition поставляется утилита cas-inspect (исполняемый файл toolchain/bin/cas-inspect), позволяющая получать сведения о содержимом KPA-пакета при работе с SDK.

Синтаксис shell-команды для запуска утилиты cas-inspect:

cas-inspect [-h|--help] [--version] {-i|--input} <PACKAGE> --verify [<COMMAND>] [-o <path>]

Параметры:

  • {-i|--input} <PACKAGE>

    Путь к KPA-пакету (файлу *.kpa).

  • <COMMAND>

    Команды:

    • dump – направляет в стандартный вывод манифест KPA-пакета и сведения о компонентах KPA-пакета, включающие размер в байтах (Size), смещение в байтах (Offset – относительно окончания манифеста KPA-пакета, Absolute – относительно начала KPA-пакета) и контрольную сумму (Digest). Этот параметр применяется по умолчанию.
    • read {manifest|blobs|<hash>} – выводит манифест KPA-пакета (read manifest), содержимое всех компонентов KPA-пакета (read blobs) или одного компонента KPA-пакета с заданной контрольной суммой (read <hash>). При использовании параметра -o <path> осуществляется вывод в файл, иначе – в стандартный вывод.
    • list – направляет в стандартный вывод контрольную сумму, смещение в байтах (относительно начала KPA-пакета) и размер в байтах для всех компонентов KPA-пакета.
    • read-files <FILES>... – выводит содержание компонента KPA-пакета по имени файла компонента. Можно указать несколько имен файлов компонентов KPA-пакета, эти имена следует разделять знаком пробела. При использовании параметра -o <path> осуществляется вывод в файл.
    • list-files – направляет в стандартный вывод список всех имен файлов компонентов KPA-пакета, которые содержатся в манифесте KPA-пакета.
  • -o <path>

    Путь к файлу или директории для сохранения данных при использовании команд read {manifest|blobs|<hash>} и read-files <FILES>.... При выводе манифеста KPA-пакета (read manifest) или содержимого компонента KPA-пакета с заданной контрольной суммой (read <hash>) нужно указать путь к файлу. При выводе содержимого всех компонентов программы (read blobs) нужно указать путь к директории, где каждый компонент программы будет сохранен в отдельном файле с именем, соответствующим контрольной сумме этого компонента. При выводе содержимого всех компонентов KPA-пакета (read-files <FILES>...) нужно указать путь к директории, где каждый компонент KPA-пакета будет сохранен в отдельном файле с именем этого компонента.

  • --verify

    Проверка наличия всех компонентов KPA-пакета, указанных в его манифесте, и отсутствия неуказанных компонентов KPA-пакета, а также расчет контрольных сумм компонентов KPA-пакета и сравнение их с указанными в манифесте KPA-пакета.

  • -h|--help

    Текст справки.

  • --version

    Версия утилиты.

Примеры shell-команд для запуска утилиты cas-inspect:

# Выводит в консоль манифест KPA-пакета и сведения о компонентах # KPA-пакета. cas-inspect -i helloworld.kpa # Выводит в консоль манифест KPA-пакета и сведения о компонентах # KPA-пакета, а также проверяет наличие компонентов KPA-пакета, # указанных в его манифесте, и контрольные суммы компонентов # KPA-пакета. cas-inspect -i helloworld.kpa --verify # Выводит в файл манифест KPA-пакета. cas-inspect -i helloworld.kpa read manifest -o ./manifest # Выводит в файл содержимое компонента KPA-пакета с заданной # контрольной суммой. cas-inspect -i helloworld.kpa read 5d8071308518a7bb003aa084fc995 d2f09b79e9e52f8cd296cb3ee2644ad3951 -o ./comp # Выводит в отдельный файл содержимое каждого компонента KPA-пакета. cas-inspect -i helloworld.kpa read blobs -o . # Выводит в консоль сведения о компонентах KPA-пакета. cas-inspect -i helloworld.kpa list
В начало
[Topic tools_cas_inspect]

Утилита cas-pm

В составе KasperskyOS Community Edition поставляется утилита cas-pm (исполняемый файл toolchain/bin/cas-pm), устанавливающая KPA-пакеты в собираемый образ решения на базе KasperskyOS.

Синтаксис shell-команды для запуска утилиты cas-pm:

cas-pm {-p|--pkgsdir} <DIR> {-d|--dbpath} <PATH> {-a|--appsdir} <DIR> [--rootdir <DIR>] [{-l|--layout} <PATH>] {-e|--extention} <ARG> {-r|--reinstall} <-v[v...]> [--sign-ext <ARG>] [--index-ext <ARG>] <PACKAGES>... [--version] [-h|--help]

Параметры:

  • {-p|--pkgsdir} <DIR>

    Путь в системе, где установлен SDK, к директории, в которой расположены KPA-пакеты для установки.

  • {-d|--dbpath} <PATH>

    Полное имя файла базы данных SQLite, которая содержит данные об установленных KPA-пакетах. Если база данных еще не создана, она будет автоматически сгенерирована при запуске утилиты с указанным именем, и в нее будет добавлена информация об устанавливаемых KPA-пакетах.

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

    1. После вызова утилиты cas-pm скопируйте файл базы данных в файловую систему, которая будет помещена в образ решения на базе KasperskyOS. Если полное имя файла базы данных изначально указывалось в такой файловой системе, этот шаг можно пропустить.
    2. Передайте полное имя файла базы данных в файловой системе, которая будет помещена в образ решения на базе KasperskyOS, через параметр DB_PATH в CMake-команду create_package_manager_entity() (подробнее см. "Сценарий использования компонента PackageManager").
  • {-a|--appsdir} <DIR>

    Путь в системе, где установлен SDK, к директории, в которую будут размещены KPA-пакеты перед записью в образ решения на базе KasperskyOS.

  • --rootdir <DIR>

    Директория, относительно которой будут установлены KPA-пакеты в образ решения на базе KasperskyOS. Директорию указывайте в той файловой системе, которая будет помещена в образ решения на базе KasperskyOS. Информация о размещении KPA-пакетов будет внесена в базу данных и потребуется компоненту PackageManager при удалении KPA-пакетов.

  • {-l|--layout} <PATH>

    Полное имя файла формата JSON, который используется для переопределения путей установки компонентов KPA-пакета. Полное имя файла указывайте в системе, где установлен SDK. По умолчанию при установке KPA-пакета его компоненты размещаются в директориях в зависимости от типа компонента KPA-пакета (подробнее смотри ключ componentType в статье "Список объектов components".). Чтобы изменить названия директорий по умолчанию, следует задать свои значения для ключей: bin, res, lib и manifestLocale. Чтобы компонент PackageManager смог обнаружить компоненты KPA-пакета после запуска решения на базе KasperskyOS, имя этого файла нужно передать в параметре CUSTOM_LAYOUT CMake-команды create_package_manager_entity() (подробнее см. "Сценарий использования компонента PackageManager").

    Пример файла custom_layout_schema.json:

    { "bin" : "custom-bin-path", "res" : "CustomResPath", "lib" : "CustomLibPath", "manifestLocale" : "Custom_manifestLocale_Path" }
  • {-e|--extention} <ARG>

    Расширение для файла KPA-пакета. Значение по умолчанию: kpa.

  • {-r|--reinstall}

    Переустановка KPA-пакетов.

  • -v[v...]

    Уровень журналирования действий, выполняемых утилитой. Количество знаков v отображает уровень журналирования. Сообщения выводятся в стандартный вывод. Возможные значения:

    • -v

      На этом уровне журналируется информация о нормальной работе утилиты без детализации, а также ошибки и предупреждения о потенциальных проблемах.

    • -vv[v...]

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

  • --sign-ext <ARG>

    Расширение для файла внешней подписи KPA-пакета. Подробнее про внешнюю подпись KPA-пакета см. "Работа с KPA-пакетами".

  • --index-ext <ARG>

    Расширение для индексного файла KPA-пакета. Подробнее про индексный файл KPA-пакета см. "Работа с KPA-пакетами".

  • <PACKAGES>

    Список полных имен устанавливаемых KPA-пакетов в системе, где установлен SDK. Расширение файла указывать не нужно. Элементы списка разделяйте знаком пробела.

  • --version

    Версия утилиты.

  • -h|--help

    Текст справки.

Примеры shell-команд для запуска утилиты cas-pm:

# Переустановить пакет helloworld.kpa, расположенный в директории, заданной переменной ${PKG_DIR}. # Пакет предварительно будет размещен в директории ${ROOTFS_DIR}/package перед записью в образ # решения на базе KasperskyOS. Директория ${ROOTFS_DIR}/package находится в файловой системе, # которая будет скопирована в образ решения. База данных repository.sqlite с информацией об # установленных в образе пакетах будет расположена в директории, заданной переменной ${ROOTFS_DIR}. # При установке ведется детальное журналирование выполняемых утилитой действий. /opt/KasperskyOS-Community-Edition-<version>/toolchain/bin/cas-pm --pkgsdir ${PKG_DIR} --reinstall -vvv --dbpath ${ROOTFS_DIR}/repository.sqlite --appsdir ${ROOTFS_DIR}/package --rootdir ${ROOTFS_DIR}/package --extension kpa ${PKG_DIR}/helloworld
В начало
[Topic tools_cas_pm]

Сценарий использования компонента PackageManager

Компонент PackageManager предоставляет API для управления KPA-пакетами в решениях, построенных на базе KasperskyOS.

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

Программный интерфейс компонента PackageManager описан в статье "Компонент PackageManager".

Добавление компонента PackageManager в решение на базе KasperskyOS

Здесь и далее клиентом называется программа, использующая API компонента PackageManager для управления KPA-пакетами.

Типовой сценарий использования компонента PackageManager включает следующие шаги:

  1. Добавление программы PackageManager в решение. Чтобы добавить PackageManager в решение, необходимо:
    find_package (package_manager REQUIRED) include_directories (${package_manager_INCLUDE}) add_subdirectory (package_manager)
    • Компонент PackageManager поставляется в составе SDK в виде набора библиотек и заголовочных файлов и собирается под конкретное решение с помощью CMake-команды create_package_manager_entity() из CMake-библиотеки package_manager.

      Чтобы собрать программу PackageManager, необходимо в корневой директории проекта создать директорию с именем package_manager, а в ней создать файл CMakeLists.txt, в котором содержится команда create_package_manager_entity().

      CMake-команда create_package_manager_entity() принимает следующие параметры:

      Обязательный параметр ENTITY, в котором указывается имя исполняемого файла для программы PackageManager.

      Опциональные параметры:

      • DEPENDS - дополнительные зависимости для сборки программы PackageManager.
      • MAIN_CONN_NAME - имя IPC-канала для соединения с процессом PackageManager. Должно совпадать со значением переменной mainConnection при обращении к API PackageManager в коде клиента.
      • ROOT_PATH - путь к корневой директории для служебных файлов программы PackageManager. значение по умолчанию: "/ROOT".
      • PKGS_DIR - путь к директории c KPA-пакетами для установки.
      • PKG_EXTENSION - расширение для файла KPA-пакета.
      • DB_PATH - полное имя файла базы данных SQLite в образе решения на базе KasperskyOS, которая содержит данные об установленных KPA-пакетах.
      • APPS_DIR - путь к директории, в которую будут устанавливаться KPA-пакеты.
      • VFS_CLIENT_LIB - имя клиентской транспортной библиотеки для подключения программы PackageManager к программе VFS.
      • NK_MODULE_NAME - путь установки заголовочных файлов компонента PackageManager в SDK относительно директории /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/. Значение по умолчанию: kl/package_manager.
      • AUDIT_CONN_NAME - имя IPC-канала для соединения с процессом AuditStorage.
      • WITHOUT_SIGN_MODE - режим учета внешней подписи: true - отсутствие внешней подписи не считается ошибкой, false - отсутствие внешней подписи считается ошибкой. Значение по умолчанию: false.
      • MANIFEST_SCHEMA_BUILD_STORE - путь к директории сборки образа решения на базе KasperskyOS со схемой манифеста.
      • MANIFEST_SCHEMA_RUNTIME_PATH - путь к директории запущенного решения на базе KasperskyOS со схемой манифеста.
      • PATH_TO_ADDITIONAL_EXTENSIONS_SCHEMAS - путь к директории с дополнительными схемами манифестов для объектов произвольного формата, заданных в значении ключа extentions манифеста KPA-пакета.
      • CUSTOM_LAYOUT - полное имя файла формата JSON, который используется для переопределения путей установки компонентов KPA-пакета.
    include (package_manager/create_package_manager_entity) create_package_manager_entity( ENTITY PkgMgrEntity NK_MODULE_NAME "package_manager" MAIN_CONN_NAME "PkgMgrEntity" ROOT_PATH "/" PKGS_DIR "/packages" PKG_EXTENSION "kpa" DB_PATH "${DB_PATH}" APPS_DIR "${APPS_PATH}" MANIFEST_SCHEMA_BUILD_STORE "${CMAKE_BINARY_DIR}/rootdir/schema" MANIFEST_SCHEMA_RUNTIME_PATH "/schema" PATH_TO_ADDITIONAL_EXTENSIONS_SCHEMAS "${CMAKE_SOURCE_DIR}/resources/additional_extensions/" CUSTOM_LAYOUT "/custom_layout_schema.json" VFS_CLIENT_LIB vfs::client AUDIT_CONN_NAME "audit_storage" WITHOUT_SIGN_MODE TRUE)
  2. Компоновка исполняемого файла клиента с клиентской прокси-библиотекой PackageManager, для чего необходимо в файле CMakeLists.txt для сборки клиента добавить следующую команду:
    target_link_libraries (<имя CMake-цели для сборки клиента> ${package_manager_CLIENT_LIBS})
  3. Добавление разрешений для необходимых событий в описание политики безопасности решения:
    1. Чтобы программа PackageManager могла управлять KPA-пакетами, политика безопасности решения должна разрешать следующие взаимодействия для класса процессов package_manager.PkgMgrEntity:
      • Доступ ко всем службам программы VFS.
      • Доступ к службам ядра Sync, VMM, Thread, HAL, Handle, FS, Notice, CM и Profiler (их описания находятся в директории sysroot-*-kos/include/kl/core из состава SDK).
    2. Чтобы клиент мог обращаться к программе PackageManager, политика безопасности решения должна разрешать следующие взаимодействия для класса клиентского процесса:
      • Доступ к соответствующим службам программы PackageManager (их описания находятся в директории sysroot-*-kos/include/kl/package_manager из состава SDK).
  4. Использование API программы PackageManager в коде клиента.

    Для этого необходимо использовать заголовочный файл component/package_manager/kos_ipc/package_manager_proxy.h. Подробнее см. "Компонент PackageManager".

В начало
[Topic pm_scenario]