Содержание
Обзор: Einit и init.yaml
Инициализирующая программа Einit
При старте ядро KasperskyOS находит в образе решения и запускает исполняемый файл с именем Einit
(инициализирующая программа). Запущенный процесс имеет класс Einit
и, как правило, используется для запуска остальных процессов, которые требуются в момент старта решения.
Генерация C-кода инициализирующей программы
В составе пакета инструментов KasperskyOS Community Edition поставляется утилита einit
, которая позволяет сгенерировать C-код инициализирующей программы на основе init-описания (файл с описанием обычно имеет имя init.yaml
). Полученная программа использует KasperskyOS API для выполнения следующих действий:
- статическое создание и запуск процессов;
- статическое создание IPC-каналов.
Стандартным способом использования утилиты einit
является интеграция ее вызова в один из шагов сборочного скрипта, в результате которого утилита einit
на основе файла init.yaml
сгенерирует файл einit.c
, содержащий код инициализирующей программы. На одном из следующих шагов сборочного скрипта необходимо скомпилировать файл einit.c
в исполняемый файл Einit
и включить в образ решения.
Для инициализирующей программы не требуется создавать файлы статических описаний. Эти файлы поставляются в составе пакета инструментов KasperskyOS Community Edition и автоматически подключаются при сборке решения. Однако класс процессов Einit
должен быть описан в файле security.psl
.
Синтаксис init.yaml
Init-описание содержит данные в формате YAML, которые идентифицируют:
- процессы, запускаемые при загрузке KasperskyOS;
- IPC-каналы, используемые процессами для взаимодействия между собой.
Эти данные представляют собой словарь с ключом entities
, содержащий список словарей процессов. Ключи словаря процесса приведены в таблице ниже.
Ключи словаря процесса в init-описании
Ключ |
Обязательный |
Значение |
---|---|---|
|
Да |
Класс безопасности процесса |
|
Нет |
Имя процесса. Если его не указывать, то будет взято имя класса безопасности. У каждого процесса должно быть уникальное имя. Можно запустить несколько процессов одного класса безопасности, но с разными именами. |
|
Нет |
Имя исполняемого файла в ROMFS (в образе решения), из которого будет запущен процесс. Если его не указывать, то будет взято имя класса безопасности без "префиксов" и точек. Например, процессы классов безопасности Можно запустить несколько процессов из одного исполняемого файла. |
|
Нет |
Список словарей IPC-каналов процесса. Этот список задает статически создаваемые IPC-каналы, клиентскими дескрипторами которых будет владеть процесс. По умолчанию список пуст. (Помимо статически создаваемых IPC-каналов процессы могут использовать динамически создаваемые IPC-каналы.) |
|
Нет |
Список аргументов, передаваемых процессу (функции |
|
Нет |
Словарь переменных окружения, передаваемых процессу. Ключами в этом словаре являются имена переменных, которым сопоставлены передаваемые значения. Максимальный размер значения – 1024 байта. |
Ключи словаря IPC-канала процесса приведены в таблице ниже.
Ключи словаря IPC-канала в init-описании
Ключ |
Обязательный |
Значение |
---|---|---|
|
Да |
Имя IPC-канала, которое может быть задано как конкретным значением, так и ссылкой вида
|
|
Да |
Имя процесса, который будет владеть серверным дескриптором IPC-канала. |
Примеры init-описаний
Здесь собраны init-описания, демонстрирующие различные аспекты запуска процессов.
В примерах в составе KasperskyOS Community Edition может использоваться формат init-описания с макросами (init.yaml.in
).
Файл с init-описанием обычно называется init.yaml
, хотя может иметь любое имя.
Соединение и запуск процесса-клиента и процесса-сервера
В следующем примере будут запущены два процесса: класса 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
Запуск двух процессов одного класса
В следующем примере будут запущены: один процесс класса 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
Передача переменных окружения и аргументов функции main()
В следующем примере будут запущены два процесса: один класса VfsFirst
(с именем по умолчанию – VfsFirst
) и второй класса VfsSecond
(с именем по умолчанию – VfsSecond
). Первый процесс при запуске получит аргумент -f /etc/fstab
, а также переменные окружения: ROOTFS
со значением ramdisk0,0 / ext2 0 и UNMAP_ROMFS
со значением 1. Второй процесс при запуске получит аргумент -l devfs /dev devfs 0
.
Имена исполняемых файлов не указаны, поэтому они будут совпадать с именами классов процессов.
Если в решении используется программа Env, то передаваемые через нее аргументы и переменные окружения переопределяют значения, заданные через init.yaml
.
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
Запуск процесса с помощью KasperskyOS API
В этом примере: использование функций EntityInitEx()
и EntityRun()
для запуска исполняемого файла из образа решения.
Ниже приводится код функции GpMgrOpenSession()
, выполняющей запуск серверного процесса, соединение его с клиентским процессом и инициализацию IPC-транспорта. Исполняемый файл нового процесса должен содержаться в ROMFS-хранилище решения.
/**
* Параметр classname задает имя класса запускаемого процесса,
* параметр server задает уникальное имя процесса, а параметр service содержит имя сервиса,
* используемое при динамическом создании канала.
* Выходной параметр transport содержит инициализированный транспорт,
* если IPC-канал до клиента успешно создан.
*/
Retcode GpMgrOpenSession(const char *classname, const char *server,
const char *service, NkKosTransport *transport)
{
Retcode rc;
Entity *e;
EntityInfo tae_info;
Handle endpoint;
rtl_uint32_t riid;
int count = CONNECT_RETRY;
/* Инициализация структуры описания процесса. */
rtl_memset(&tae_info, 0, sizeof(tae_info));
tae_info.eiid = classname;
tae_info.args[0] = server;
tae_info.args[1] = service;
/* Создание процесса с описанием tae_info и именем server.
* Поскольку третий параметр равен RTL_NULL, имя запускаемого
* бинарного файла совпадает с именем класса из описания tae_info.
* Созданный процесс находится в остановленном состоянии. */
if ((e = EntityInitEx(&tae_info, server, RTL_NULL)) == NK_NULL)
{
rtl_printf("Cannot init entity '%s'\n", tae_info.eiid);
return rcFail;
}
/* Запуск процесса. */
if ((rc = EntityRun(e)) != rcOk)
{
rtl_printf("Cannot launch entity %" RTL_PRId32 "\n", rc);
EntityFree(e);
return rc;
}
/* Динамическое создание IPC-канала. */
while ((rc = KnCmConnect(server, service, INFINITE_TIMEOUT, &endpoint, &riid) ==
rcResourceNotFound && count--)
{
KnSleep(CONNECT_DELAY);
}
if (rc != rcOk)
{
rtl_printf("Cannot connect to server %" RTL_PRId32 "\n", rc);
return rc;
}
/* Инициализация IPC-транспорта. */
NkKosTransport_Init(transport, endpoint, NK_NULL, 0);
...
return rcOk;
}
Чтобы процесс мог запускать другие процессы, политика безопасности решения должна разрешать ему использование следующих служб ядра: Handle
, Task
и VMM
(их описания находятся в директории kl\core\
).
Обзор: программа Env
Служебная программа Env
предназначена для передачи аргументов и переменных окружения запускаемым процессам. При запуске каждый процесс автоматически отправляет запрос процессу Env
и получает необходимые данные.
Обращение процесса к Env
переопределяет аргументы и переменные окружения, полученные через Einit
.
Чтобы использовать программу Env
в своем решении, необходимо:
1. Разработать код программы Env
, используя макросы из env/env.h
.
2. Собрать бинарный файл программы Env
, скомпоновав ее с библиотекой env_server
.
3. В init-описании указать, что необходимо запустить процесс Env
и соединить с ней выбранные процессы (Env
при этом является сервером). Имя канала задается макросом ENV_SERVICE_NAME
, объявленным в файле env/env.h
.
4. Включить бинарный файл Env
в образ решения.
Код программы Env
В коде программы Env
используются следующие макросы и функции, объявленные в файле env/env.h
:
ENV_REGISTER_ARGS(name,argarr)
– передать процессу с именемname
аргументы из массиваargarr
(максимальный размер одного элемента – 256 байтов);ENV_REGISTER_VARS(name,envarr)
– передать процессу с именемname
переменные окружения из массиваenvarr
(максимальный размер одного элемента – 256 байтов);ENV_REGISTER_PROGRAM_ENVIRONMENT(name,argarr,envarr)
– передать процессу с именемname
как аргументы, так и переменные окружения;envServerRun()
– инициализировать серверную часть программыEnv
, чтобы она могла отвечать на запросы.
Передача переменных окружения и аргументов с помощью Env
Пример передачи аргументов при запуске процесса
Ниже приводится код программы Env
, которая при запуске процесса с именем NetVfs
передаст ему три аргумента: NetVfs
, -l devfs /dev devfs 0
и -l romfs /etc romfs 0
:
env.c
int main(int argc, char** argv)
{
const char* NetVfsArgs[] = {
"-l", "devfs /dev devfs 0",
"-l", "romfs /etc romfs 0"
};
ENV_REGISTER_ARGS("NetVfs", NetVfsArgs);
envServerRun();
return EXIT_SUCCESS;
}
Пример передачи переменных окружения при запуске процесса
Ниже приводится код программы Env
, которая при запуске процесса с именем Vfs3
передаст ему две переменных окружения: ROOTFS=ramdisk0,0 / ext2 0
и UNMAP_ROMFS=1
:
env.c
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;
}