KasperskyOS Community Edition 1.1
[Topic sc_application_start]

Обзор: 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-описании

Ключ

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

Значение

name

Да

Класс безопасности процесса

task

Нет

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

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

path

Нет

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

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

connections

Нет

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

args

Нет

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

env

Нет

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

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

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

Ключ

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

Значение

id

Да

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

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

target

Да

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

В начало
[Topic einit_overview]

Примеры 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

В начало
[Topic using_einit]

Запуск процесса с помощью KasperskyOS API

В этом примере: использование функций EntityInitEx() и EntityRun() для запуска исполняемого файла из образа решения.

Ниже приводится код функции GpMgrOpenSession(), выполняющей запуск серверного процесса, соединение его с клиентским процессом и инициализацию IPC-транспорта. Исполняемый файл нового процесса должен содержаться в ROMFS-хранилище решения.

#define CONNECT_RETRY 150 /* Количество попыток соединения */

#define CONNECT_DELAY 10 /* Задержка в мс между попытками */

/**

* Параметр 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\).

В начало
[Topic app_static_start]

Обзор: программа 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

В начало
[Topic env_overview]

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

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

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

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 0"

};

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]