Contents
- Development for KasperskyOS
- Starting processes
- File systems and network
- Contents of the VFS component
- Creating an IPC channel to VFS
- Including VFS functionality in a program
- Overview: startup parameters and environment variables of VFS
- Mounting file systems when VFS starts
- Using VFS backends to separate data streams
- Creating a VFS backend
- Dynamically configuring the network stack
- IPC and transport
Overview: Einit and init.yaml
Einit initializing program
When a solution is started, the KasperskyOS kernel finds the executable file named Einit
(initializing program) in the solution image and runs this executable file. The initializing program performs the following operations:
- Creates and starts processes when a solution is started.
- Creates IPC channels between processes when a solution is started (statically creates IPC channels).
A process of the initializing program belongs to the Einit
class.
Generating the source code of the initializing program
The KasperskyOS SDK includes the einit
tool, which generates the C-language source code of the initializing program. The standard way to use the einit
tool is to integrate an einit call into one of the steps of the build script, which generates the einit.c
file containing the source code of the initializing program. In one of the following steps of the build script, you must compile the einit.c
file into the executable file of Einit
and include it into the solution image.
You are not required to create formal specification files for the initializing program. These files are provided in the KasperskyOS SDK and are automatically applied during a solution build. However, the Einit
process class must be specified in the security.psl
file.
The einit
tool generates the source code of the initializing program based on the init description, which consists of a text file that is usually named init.yaml
.
Syntax of init.yaml
An init description contains data in YAML format. This data identifies the following:
- Processes that are started when the solution starts.
- IPC channels that are created when the solution starts and are used by processes to interact with each other (not with the kernel).
This data consists of a dictionary with the entities
key containing a list of dictionaries of processes. Process dictionary keys are presented in the table below.
Process dictionary keys in an init description
Key |
Required |
Value |
---|---|---|
|
Yes |
Process class name (from the EDL description). |
|
No |
Process name. If this name is not specified, the process class name will be used. Each process must have a unique name. You can start multiple processes of the same class if they have different names. |
|
No |
Name of the executable file in ROMFS (in the solution image). If this name is not specified, the process class name (without prefixes and dots) will be used. For example, processes of the You can start multiple processes from the same executable file. |
|
No |
Process IPC channel dictionaries list. This list defines the statically created IPC channels whose client IPC handles will be owned by the process. The list is empty by default. (In addition to statically created IPC channels, processes can also use dynamically created IPC channels.) |
|
No |
List of program startup parameters ( |
|
No |
Dictionary of program environment variables. The keys in this dictionary are the names of environment variables. The maximum size of an environment variable value is 1024 bytes. |
Process IPC channel dictionary keys are presented in the table below.
IPC channel dictionary keys in an init description
Key |
Required |
Value |
---|---|---|
|
Yes |
IPC channel name, which can be defined as a specific value or as a link such as
|
|
Yes |
Name of the process that will own the server handle of the IPC channel. |
Example init descriptions
This section provides examples of init descriptions that demonstrate various aspects of starting processes.
The build system can automatically create an init description based on the init.yaml.in
template.
Starting a client and server and creating an IPC channel between them
In this example, a process of the Client
class and a process of the Server
class are started. The names of the processes are not specified, so they will match the names of their respective process classes. The names of the executable files are not specified, so they will also match the names of their respective process classes. The processes will be connected by an IPC channel named server_connection
.
init.yaml
Starting processes from defined executable files
This example will start a Client
-class process from the executable file named cl
, a ClientServer
-class process from the executable file named csr
, and a MainServer
-class process from the executable file named msr
. The names of the processes are not specified, so they will match the names of their respective process classes.
init.yaml
Starting two processes from the same executable file
This example will start a Client
-class process from the executable file named Client
, and two processes of the MainServer
and BkServer
classes from the executable file named srv
. The names of the processes are not specified, so they will match the names of their respective process classes.
init.yaml
Starting two servers of the same class and a client, and creating IPC channels between the client and servers
This example will start a Client
-class process (named Client
) and two Server
-class processes named UserServer
and PrivilegedServer
. The client will be connected to the servers via IPC channels named server_connection_us
and server_connection_ps
. The names of the executable files are not specified, so they will match the names of their respective process classes.
init.yaml
Setting the startup parameters and environment variables of programs
This example will start a VfsFirst
-class process (named VfsFirst
) and a VfsSecond
-class process (named VfsSecond
). The program that will run in the context of the VfsFirst
process will be started with the parameter -f /etc/fstab
, and will receive the ROOTFS
environment variable with the value ramdisk0,0 / ext2 0
and the UNMAP_ROMFS
environment variable with the value 1
. The program that will run in the context of the VfsSecond
process will be started with the -l devfs /dev devfs 0
parameter. The names of the executable files are not specified, so they will match the names of their respective process classes.
init.yaml
Starting processes using the system program ExecutionManager
The ExecutionManager component provides a C++ interface for creating, starting and stopping processes in solutions that are based on KasperskyOS.
The interface of the ExecutionManager component is not suitable for use in code that is written in C. To manage processes in the C language, use the task.h interface of the libkos library.
The ExecutionManager component API is an add-on over IPC that helps simplify the program development process. ExecutionManager is a separate system program that is accessed through IPC. However, developers are provided with a client library that eliminates the necessity of directly using IPC calls.
The programming interface of the ExecutionManager component is described in the article titled "ExecutionManager component".
ExecutionManager component usage scenario
Hereinafter "the client" refers to the application that uses the ExecutionManager component API to manage other applications.
The typical usage scenario for the ExecutionManager component includes the following steps:
- Add the ExecutionManager program to a solution. To add ExecutionManager to a solution:
- Add the following commands to the CMakeLists.txt root file:
find_package (execution_manager REQUIRED) include_directories (${execution_manager_INCLUDE}) add_subdirectory (execution_manager)The
BlobContainer
program is required for the ExecutionManager program to work properly. This program is automatically added to a solution when adding ExecutionManager.- The ExecutionManager component is provided in the SDK as a set of static libraries and header files, and is built for a specific solution by using the CMake command
create_execution_manager_entity()
from the CMake libraryexecution_manager
.To build the ExecutionManager program, create a directory named
execution_manager
in the root directory of the project. In the new directory, create aCMakeLists.txt
file containing thecreate_execution_manager_entity()
command.The CMake command
create_execution_manager_entity()
takes the following parameters:Mandatory
ENTITY
parameter that specifies the name of the executable file for the ExecutionManager program.Optional parameters:
DEPENDS
– additional dependencies for building the ExecutionManager program.MAIN_CONN_NAME
– name of the IPC channel for connecting to the ExecutionManager process. It must match the value of themainConnection
variable when calling the ExecutionManager API in the client code.ROOT_PATH
– path to the root directory for service files of the ExecutionManager program. The default root path is"/ROOT"
.VFS_CLIENT_LIB
– name of the client transport library used to connect the ExecutionManager program to theVFS
program.
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})- When building a solution (CMakeLists.txt file for the Einit program), add the following executable files to the solution image:
- Executable file of the ExecutionManager program
- Executable file of the
BlobContainer
program
- Link the client executable file to the client proxy library of ExecutionManager by adding the following command to the
CMakeLists.txt
file for building the client:target_link_libraries (<name of the CMake target for building the client> ${execution_manager_EXECMGR_PROXY}) - Add permissions for the necessary events to the solution security policy description:
- To enable the ExecutionManager program to run other processes, the solution security policy must allow the following interactions for the
execution_manager.ExecMgrEntity
process class:- Security events of the
execute
type for all classes of processes that will be run. - Access to all endpoints of the
VFS
program. - Access to all endpoints of the
BlobContainer
program. - Access to the core endpoints
Sync
,Task
,VMM
,Thread
,HAL
,Handle
,FS
,Notice
,CM
andProfiler
(their descriptions are located in the directorysysroot-*-kos/include/kl/core
from the SDK).
- Security events of the
- To enable a client to call the ExecutionManager program, the solution security policy must allow the following interactions for the client process class:
- Access to the appropriate endpoints of the ExecutionManager program (their descriptions are located in the directory
sysroot-*-kos/include/kl/execution_manager
from the SDK).
- Access to the appropriate endpoints of the ExecutionManager program (their descriptions are located in the directory
- To enable the ExecutionManager program to run other processes, the solution security policy must allow the following interactions for the
- Use of the ExecutionManager program API in the client code.
Use the header file
component/package_manager/kos_ipc/package_manager_proxy.h
for this. For more details, refer to "ExecutionManager component".
Overview: Env program
The system program Env
is intended for setting startup parameters and environment variables of programs. If the Env
program is included in a solution, the processes connected via IPC channel to the Env
process will automatically send IPC requests to this program and receive startup parameters and environment variables when these processes are started.
Use of the Env
system program is an outdated way of setting startup parameters and environment variables of programs. Instead, you must set the startup parameters and environment variables of programs via the init.yaml.in
or init.yaml
file.
If the value of a startup parameter or environment variable of a program is defined through the Env
program and via the init.yaml.in
or init.yaml
file, the value defined through the Env
program will be applied.
To use the Env
program in a solution, you need to do the following:
- Develop the code of the
Env
program using the macros and functions from the header filesysroot-*-kos/include/env/env.h
from the KasperskyOS SDK. - Build the executable file of the
Env
program by linking it to theenv_server
library from the KasperskyOS SDK. - In the init description, indicate that the
Env
process must be started and connected to other processes (Env
acts as a server in this case). The name of the IPC channel is assigned by theENV_SERVICE_NAME
macro defined in the header fileenv.h
. - Include the
Env
executable file in the solution image.
Source code of the Env program
The source code of the Env
program utilizes the following macros and functions from the header file env.h
:
ENV_REGISTER_ARGS(name,argarr)
sets theargarr
startup parameters for the program that will run in the context of thename
process.ENV_REGISTER_VARS(name,envarr)
sets theenvarr
environment variables for the program that will run in the context of thename
process.ENV_REGISTER_PROGRAM_ENVIRONMENT(name,argarr,envarr)
sets theargarr
startup parameters andenvarr
environment variables for the program that will run in the context of thename
process.envServerRun()
initializes the server part of theEnv
program so that it can respond to IPC requests.
Examples of using Env to set the startup parameters and environment variables of programs
Use of the Env
system program is an outdated way of setting startup parameters and environment variables of programs. Instead, you must set the startup parameters and environment variables of programs via the init.yaml.in
or init.yaml
file.
If the value of a startup parameter or environment variable of a program is defined through the Env
program and via the init.yaml.in
or init.yaml
file, the value defined through the Env
program will be applied.
Example of setting startup parameters of a program
Source code of the Env
program is presented below. When the process named NetVfs
starts, the program passes the following two program startup parameters to this process: -l devfs /dev devfs 0
and -l romfs /etc romfs ro
:
env.c
Example of setting environment variables of a program
Source code of the Env
program is presented below. When the process named Vfs3
starts, the program passes the following two program environment variables to this process: ROOTFS=ramdisk0,0 / ext2 0
and UNMAP_ROMFS=1
:
env.c
File systems and network
In KasperskyOS, operations with file systems and the network are executed via a separate system program that implements a virtual file system (VFS).
In the SDK, the VFS component consists of a set of executable files, libraries, formal specification files, and header files. For more details, see the Contents of the VFS component section.
The main scenario of interaction with the VFS system program includes the following:
- An application connects via IPC channel to the VFS system program and then links to the client library of the VFS component during the build.
- In the application code, POSIX calls for working with file systems and the network are converted into client library function calls.
Input and output to file handles for standard I/O streams (stdin, stdout and stderr) are also converted into queries to the VFS. If the application is not linked to the client library of the VFS component, printing to stdout is not possible. If this is the case, you can only print to the standard error stream (stderr), which in this case is performed via special methods of the KasperskyOS kernel without using VFS.
- The client library makes IPC requests to the VFS system program.
- The VFS system program receives an IPC requests and calls the corresponding file system implementations (which, in turn, may make IPC requests to device drivers) or network drivers.
- After the request is handled, the VFS system program responds to the IPC requests of the application.
Using multiple VFS programs
Multiple copies of the VFS system program can be added to a solution for the purpose of separating the data streams of different system programs and applications. You can also separate the data streams within one application. For more details, refer to Using VFS backends to separate data streams.
Adding VFS functionality to an application
The complete functionality of the VFS component can be included in an application, thereby avoiding the need to pass each request via IPC. For more details, refer to Including VFS functionality in a program.
However, use of VFS functionality via IPC enables the solution developer to do the following:
- Use a solution security policy to control method calls for working with the network and file systems.
- Connect multiple client programs to one VFS program.
- Connect one client program to two VFS programs to separately work with the network and file systems.
Contents of the VFS component
The VFS component implements the virtual file system. In the KasperskyOS SDK, the VFS component consists of a set of executable files, libraries, formal specification files and header files that enable the use of file systems and/or a network stack.
VFS libraries
The vfs
CMake package contains the following libraries:
vfs_fs
contains implementations of the devfs, ramfs and ROMFS file systems, and adds implementations of other file systems to VFS.vfs_net
contains the implementation of the devfs file system and the network stack.vfs_imp
contains thevfs_fs
andvfs_net
libraries.vfs_remote
is the client transport library that converts local calls into IPC requests to VFS and receives IPC responses.vfs_server
is the VFS server transport library that receives IPC requests, converts them into local calls, and sends IPC responses.vfs_local
is used to include VFS functionality in a program.
VFS executable files
The precompiled_vfs
CMake package contains the following executable files:
VfsRamFs
VfsSdCardFs
VfsNet
The VfsRamFs
and VfsSdCardFs
executable files include the vfs_server
, vfs_fs
, vfat
and lwext4
libraries. The VfsNet
executable file includes the vfs_server
, vfs_imp
libraries.
Each of these executable files has its own default values for startup parameters and environment variables.
Formal specification files and header files of VFS
The sysroot-*-kos/include/kl
directory from the KasperskyOS SDK contains the following VFS files:
- Formal specification files
VfsRamFs.edl
,VfsSdCardFs.edl
,VfsNet.edl
andVfsEntity.edl
, and the header files generated from them. - Formal specification file
Vfs.cdl
and the header fileVfs.cdl.h
generated from it. - Formal specification files
Vfs*.idl
and the header files generated from them.
Libc library API supported by VFS
VFS functionality is available to programs through the API provided by the libc
library.
The functions implemented by the vfs_fs
and vfs_net
libraries are presented in the table below. The *
character denotes the functions that are optionally included in the vfs_fs
library (depending on the library build parameters).
Functions implemented by the vfs_fs library
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Functions implemented by the vfs_net library
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
If there is no implementation of a called function in VFS, the EIO
error code is returned.
Creating an IPC channel to VFS
In this example, the Client
process uses the file systems and network stack, and the VfsFsnet
process handles the IPC requests of the Client
process related to the use of file systems and the network stack. This approach is utilized when there is no need to separate data streams related to file systems and the network stack.
The IPC channel name must be assigned by the _VFS_CONNECTION_ID
macro defined in the header file sysroot-*-kos/include/vfs/defs.h
from the KasperskyOS SDK.
Init description of the example:
init.yaml
Including VFS functionality in a program
In this example, the Client
program includes the VFS program functionality for working with the network stack (see the figure below).
VFS component libraries in a program
The client.c
implementation file is compiled and the vfs_local
, vfs_implementation
and dnet_implementation
libraries are linked:
CMakeLists.txt
In case the Client
program uses file systems, you must link the vfs_local
and vfs_fs
libraries, and the libraries for implementing these file systems. In this case, you must also add a block device driver to the solution.
Overview: startup parameters and environment variables of VFS
VFS program startup parameters
-l <entry in fstab format>
The startup parameter
-l
mounts the defined file system.-f <path to fstab file>
The parameter
-f
mounts the file systems specified in thefstab
file. If theUNMAP_ROMFS
environment variable is not defined, thefstab
file will be sought in the ROMFS image. If theUNMAP_ROMFS
environment variable is defined, thefstab
file will be sought in the file system defined through theROOTFS
environment variable.
Examples of using VFS program startup parameters
Environment variables of the VFS program
UNMAP_ROMFS
If the
UNMAP_ROMFS
environment variable is defined, the ROMFS image will be deleted from memory. This helps conserve memory. When using the startup parameter-f
, it also provides the capability to search for thefstab
file in the file system defined through theROOTFS
environment variable instead of searching the ROMFS image.ROOTFS = <entry in fstab format>
The
ROOTFS
environment variable mounts the defined file system to the root directory. When using the startup parameter-f
, a combination of theROOTFS
andUNMAP_ROMFS
environment variables provides the capability to search for thefstab
file in the file system defined through theROOTFS
environment variable instead of searching the ROMFS image.VFS_CLIENT_MAX_THREADS
The
VFS_CLIENT_MAX_THREADS
environment variable redefines the SDK configuration parameterVFS_CLIENT_MAX_THREADS
._VFS_NETWORK_BACKEND=<VFS backend name>:<name of the IPC channel to the VFS process>
The
_VFS_NETWORK_BACKEND
environment variable defines the VFS backend for working with the network stack. You can specify the name of the standard VFS backend:client
(for a program that runs in the context of a client process),server
(for a VFS program that runs in the context of a server process) orlocal
, and the name of a custom VFS backend. If thelocal
VFS backend is used, the name of the IPC channel is not specified (_VFS_NETWORK_BACKEND=local:
). You can specify more than one IPC channel by separating them with a comma._VFS_FILESYSTEM_BACKEND=<VFS backend name>:<name of the IPC channel to the VFS process>
The
_VFS_FILESYSTEM_BACKEND
environment variable defines the VFS backend for working with file systems. The name of the VFS backend and the name of the IPC channel to the VFS process are defined the same way as they are defined in the_VFS_NETWORK_BACKEND
environment variable.
Default values for startup parameters and environment variables of VFS
For the VfsRamFs
executable file:
For the VfsSdCardFs
executable file:
For the VfsNet
executable file:
Mounting file systems when VFS starts
When the VFS program starts, only the RAMFS file system is mounted to the root directory by default. If you need to mount other file systems, this can be done not only by calling the mount()
function but also by setting the startup parameters and environment variables of the VFS program.
The ROMFS
and squashfs
file systems are intended for read-only operations. For this reason, you must specify the ro
parameter to mount these file systems.
Using the startup parameter -l
One way to mount a file system is to set the startup parameter -l <entry in fstab format>
for the VFS program.
In these examples, the devfs and ROMFS file systems will be mounted when the VFS program is started:
init.yaml.(in)
CMakeLists.txt
Using the fstab file from the ROMFS image
When building a solution, you can add the fstab
file to the ROMFS image. This file can be used to mount file systems by setting the startup parameter -f <path to the fstab file>
for the VFS program.
In these examples, the file systems defined via the fstab
file that was added to the ROMFS image during the solution build will be mounted when the VFS program is started:
init.yaml.(in)
CMakeLists.txt
Using an "external" fstab file
If the fstab
file resides in another file system instead of in the ROMFS image, you must set the following startup parameters and environment variables for the VFS program to enable use of this file:
ROOTFS
. This environment variable mounts the file system containing thefstab
file to the root directory.UNMAP_ROMFS
. If this environment variable is defined, thefstab
file will be sought in the file system defined through theROOTFS
environment variable.-f
. This startup parameter is used to mount the file systems specified in thefstab
file.
In these examples, the ext2 file system that should contain the fstab
file at the path /etc/fstab
will be mounted to the root directory when the VFS program starts:
init.yaml.(in)
CMakeLists.txt
Using VFS backends to separate data streams
This example employs a secure development pattern that separates data streams related to file system use from data streams related to the use of a network stack.
The Client
process uses file systems and the network stack. The VfsFirst
process works with file systems, and the VfsSecond
process provides the capability to work with the network stack. The environment variables of programs that run in the contexts of the Client
, VfsFirst
and VfsSecond
processes are used to define the VFS backends that ensure the segregated use of file systems and the network stack. As a result, IPC requests of the Client
process that are related to the use of file systems are handled by the VfsFirst
process, and IPC requests of the Client
process that are related to network stack use are handled by the VfsSecond
process (see the figure below).
Process interaction scenario
Init description of the example:
init.yaml
Creating a VFS backend
This example demonstrates how to create and use a custom VFS backend.
The Client
process uses the fat32 and ext4 file systems. The VfsFirst
process works with the fat32 file system, and the VfsSecond
process provides the capability to work with the ext4 file system. The environment variables of programs that run in the contexts of the Client
, VfsFirst
and VfsSecond
processes are used to define the VFS backends ensuring that IPC requests of the Client
process are handled by the VfsFirst
or VfsSecond
process depending on the specific file system being used by the Client
process. As a result, IPC requests of the Client
process related to use of the fat32 file system are handled by the VfsFirst
process, and IPC requests of the Client
process related to use of the ext4 file system are handled by the VfsSecond
process (see the figure below).
On the VfsFirst
process side, the fat32 file system is mounted to the directory /mnt1
. On the VfsSecond
process side, the ext4 file system is mounted to the directory /mnt2
. The custom VFS backend custom_client
used on the Client
process side sends IPC requests over the IPC channel VFS1
or VFS2
depending on whether or not the file path begins with /mnt1
. The custom VFS backend uses the standard VFS backend client
as an intermediary.
Process interaction scenario
Source code of the VFS backend
This implementation file contains the source code of the VFS backend custom_client
, which uses the standard client
VFS backends:
backend.c
Linking the Client program
Creating a static VFS backend library:
CMakeLists.txt
Linking the Client
program to the static VFS backend library:
CMakeLists.txt
Setting the startup parameters and environment variables of programs
Init description of the example:
init.yaml
Dynamically configuring the network stack
To change the default network stack parameters, use the sysctl()
function or sysctlbyname()
function that are declared in the header file sysroot-*-kos/include/sys/sysctl.h
from the KasperskyOS SDK. The parameters that can be changed are presented in the table below.
Configurable network stack parameters
Parameter name |
Parameter description |
---|---|
|
Maximum time to live (TTL) of sent IP packets. It does not affect the ICMP protocol. |
|
If its value is set to |
|
MSS value (in bytes) that is applied if only the communicating side failed to provide this value when opening the TCP connection, or if "Path MTU Discovery" mode (RFC 1191) is not enabled. This MSS value is also forwarded to the communicating side when opening a TCP connection. |
|
Minimum MSS value, in bytes. |
|
If its value is set to |
|
Number of times to send test messages (Keep-Alive Probes, or KA) without receiving a response before the TCP connection will be considered closed. If its value is set to |
|
TCP connection idle period, after which keep-alive probes begin. This is defined in conditional units, which can be converted into seconds via division by the |
|
Time interval between recurring keep-alive probes when no response is received. This is defined in conditional units, which can be converted into seconds via division by the |
|
Size of the buffer (in bytes) for data received over the TCP protocol. |
|
Size of the buffer (in bytes) for data sent over the TCP protocol. |
|
Size of the buffer (in bytes) for data received over the UDP protocol. |
|
Size of the buffer (in bytes) for data sent over the UDP protocol. |
MSS configuration example:
Creating IPC channels
There are two methods for creating IPC channels: static and dynamic.
Static creation of IPC channels is simpler to implement because you can use the init description for this purpose.
Dynamic creation of IPC channels lets you change the topology of interaction between processes on the fly. This is necessary if it is unknown which specific server provides the endpoint required by the client. For example, you may not know which specific drive you will need to write data to.
Statically creating an IPC channel
Static creation of IPC channels has the following characteristics:
- The client and server are not yet running when the IPC channel is created.
- Creation of an IPC channel is performed by the parent process that starts the client and server (this is normally Einit).
- A deleted IPC channel cannot be restored.
- To get the IPC handle and the endpoint ID (riid) after an IPC channel is created, the client and server must use the API defined in the header file
sysroot-*-kos/include/coresrv/sl/sl_api.h
from the KasperskyOS SDK.
The IPC channels defined in the init description are statically created.
Dynamically creating an IPC channel
Dynamic creation of IPC channels has the following characteristics:
- The client and server are already running when the IPC channel is created.
- The IPC channel is jointly created by the client and server.
- A new IPC channel may be created in place of a deleted one.
- The client and server get the IPC handle and endpoint ID (riid) immediately after the IPC channel is successfully created.
Adding an endpoint from KasperskyOS Community Edition to a solution
To ensure that a Client
program can use some specific functionality via the IPC mechanism, the following is required:
- In KasperskyOS Community Edition, find the executable file (we'll call it
Server
) that implements the necessary functionality. (The term "functionality" used here refers to one or more endpoints that have their own IPC interfaces.) - Include the CMake package containing the
Server
file and its client library. - Add the
Server
executable file to the solution image. - Edit the init description so that when the solution starts, the
Einit
program starts a new server process from theServer
executable file and connects it, using an IPC channel, to the process started from theClient
file.You must indicate the correct name of the IPC channel so that the transport libraries can identify this channel and find its IPC handles. The correct name of the IPC channel normally matches the name of the server process class. VFS is an exception in this case.
- Edit the PSL description to allow startup of the server process and IPC interaction between the client and the server.
- In the source code of the
Client
program, include the server methods header file. - Link the
Client
program with the client library.
Example of adding a GPIO driver to a solution
KasperskyOS Community Edition includes a gpio_hw
file that implements GPIO driver functionality.
The following commands connect the gpio CMake package:
.\CMakeLists.txt
The gpio_hw
executable file is added to a solution image by using the gpio_HW_ENTITY
variable, whose name can be found in the configuration file of the package at /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/lib/cmake/gpio/gpio-config.cmake:
einit\CMakeLists.txt
The following strings need to be added to the init description:
init.yaml.in
The following strings need to be added to the PSL description:
security.psl.in
In the code of the Client
program, you need to include the header file in which the GPIO driver methods are declared:
client.c
Finally, you need to link the Client
program with the GPIO client library:
client\CMakeLists.txt
To ensure correct operation of the GPIO driver, you may need to add the BSP component to the solution. To avoid overcomplicating this example, BSP is not examined here. For more details, see the gpio_output example: /opt/KasperskyOS-Community-Edition-<version>/examples/gpio_output
Overview: IPC message structure
In KasperskyOS, all interactions between processes have statically defined types. The permissible structures of an IPC message are defined by the IDL descriptions of servers.
An IPC message (request and response) contains a constant part and an (optional) arena.
Constant part of an IPC message
The constant part of an IPC message contains the RIID, MID, and (optionally) fixed-size parameters of interface methods.
Fixed-size parameters are parameters that have IDL types of a fixed size.
The RIID and MID identify the interface and method being called:
- The RIID (Runtime Implementation ID) is the sequence number of the utilized endpoint within the set of server endpoints (starting at zero).
- The MID (Method ID) is the sequence number of the called method within the set of methods of the utilized endpoint (starting at zero).
The type of the constant part of the IPC message is generated by the NK compiler based on the IDL description of the interface. A separate structure is generated for each interface method. Union
types are also generated for storing any request to a process, component or interface. For more details, refer to Example generation of transport methods and types.
IPC message arena
An IPC message arena (hereinafter also referred to as an arena) contains variable-size parameters of interface methods (and/or elements of these parameters).
Variable-size parameters are parameters that have IDL types of a variable size.
For more details, refer to "Working with an IPC message arena".
Maximum IPC message size
The maximum size of an IPC message is determined by the KasperskyOS kernel parameters. On most hardware platforms supported by KasperskyOS, the cumulative size of the constant part and arena of an IPC message cannot exceed 4, 8, or 16 MB.
IPC message structure verification by the security module
Prior to querying IPC message-related rules, the Kaspersky Security Module verifies that the sent IPC message is correct. Requests and responses are both validated. If the IPC message has an incorrect structure, it will be rejected without calling the security model methods associated with it.
Implementation of IPC interaction
To make it easier for a developer to implement IPC interaction, KasperskyOS Community Edition provides the following:
- NK compiler that generates transport methods and types.
Libkos
library that provides the API for working with IPC transport.
Implementation of simple IPC interaction is demonstrated in the echo and ping examples (/opt/KasperskyOS-Community-Edition-<version>/examples/
).
Getting an IPC handle
The client and server IPC handles must be obtained if there are no ready-to-use transport libraries for the utilized endpoint (for example, if you wrote your own endpoint). To independently work with IPC transport, you need to first initialize it by using the NkKosTransport_Init()
method and pass the IPC handle of the utilized channel as the second argument.
For more details, see the echo and ping examples (/opt/KasperskyOS-Community-Edition-<version>/examples/
).
You do not need to get an IPC handle to utilize endpoints that are implemented in executable files provided in KasperskyOS Community Edition. The provided transport libraries are used to perform all transport operations, including obtaining IPC handles.
See the gpio_*, net_*, net2_* and multi_vfs_* examples (/opt/KasperskyOS-Community-Edition-<version>/examples/
).
Getting an IPC handle when statically creating a channel
When statically creating an IPC channel, both the client and server can obtain their IPC handles immediately after startup by using the ServiceLocatorRegister()
and ServiceLocatorConnect()
methods and specifying the name of the created IPC channel.
For example, if the IPC channel is named server_connection
, the following must be called on the client side:
The following must be called on the server side:
For more details, see the echo and ping examples (/opt/KasperskyOS-Community-Edition-<version>/examples/
), and the header file /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/sl/sl_api.h
.
Closing an obtained IPC handle will cause the IPC channel to become unavailable. After an IPC handle is closed, it is impossible to obtain it again or restore access to the IPC channel.
Getting an IPC handle when dynamically creating a channel
Both the client and server receive their own IPC handles immediately after dynamic creation of an IPC channel is successful.
The client IPC handle is one of the output (out
) arguments of the KnCmConnect()
method. The server IPC handle is an output argument of the KnCmAccept()
method. For more details, see the header file /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/cm/cm_api.h
.
If the dynamically created IPC channel is no longer required, its client and server handles should be closed. The IPC channel can be created again if necessary.
Page topGetting an endpoint ID (riid)
The endpoint ID (riid) must be obtained on the client side if there are no ready-to-use transport libraries for the utilized endpoint (for example, if you wrote your own endpoint). To call methods of the server, you must first call the proxy object initialization method on the client side and pass the endpoint ID as the third parameter. For example, for the Filesystem
interface:
For more details, see the echo and ping examples (/opt/KasperskyOS-Community-Edition-<version>/examples/
).
You do not need to get the endpoint ID to utilize endpoints that are implemented in executable files provided in KasperskyOS Community Edition. The provided transport libraries are used to perform all transport operations.
See the gpio_*, net_*, net2_* and multi_vfs_* examples (/opt/KasperskyOS-Community-Edition-<version>/examples/
).
Getting an endpoint ID when statically creating a channel
When statically creating an IPC channel, the client can obtain the ID of the necessary endpoint by using the ServiceLocatorGetRiid()
method and specifying the IPC channel handle and the qualified name of the endpoint. For example, if the OpsComp
component instance provides the FS
endpoint, the following must be called on the client side:
For more details, see the echo and ping examples (/opt/KasperskyOS-Community-Edition-<version>/examples/
), and the header file /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/sl/sl_api.h
.
Getting an endpoint ID when dynamically creating a channel
The client receives the endpoint ID immediately after dynamic creation of an IPC channel is successful. The client IPC handle is one of the output (out
) arguments of the KnCmConnect()
method. For more details, see the header file /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/cm/cm_api.h
.
Example generation of transport methods and types
When building a solution, the NK compiler uses the EDL, CDL and IDL descriptions to generate a set of special methods and types that simplify the creation, forwarding, receipt and processing of IPC messages.
As an example, we will examine the Server
process class that provides the FS
endpoint, which contains a single Open()
method:
Server.edl
Operations.cdl
Filesystem.idl
These descriptions will be used to generate the files named Server.edl.h
, Operations.cdl.h
, and Filesystem.idl.h
, which contain the following methods and types:
Methods and types that are common to the client and server
- Abstract interfaces containing the pointers to the implementations of the methods included in them.
In our example, one abstract interface (
Filesystem
) will be generated: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; - Set of interface methods.
When calling an interface method, the corresponding values of the RIID and MID are automatically inserted into the request.
In our example, a single
Filesystem_Open
interface method will be generated: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)
Methods and types used only on the client
- Types of proxy objects.
A proxy object is used as an argument in an interface method. In our example, a single
Filesystem_proxy
proxy object type will be generated:typedef struct Filesystem_proxy { struct Filesystem base; struct nk_transport *transport; nk_iid_t iid; } Filesystem_proxy; - Functions for initializing proxy objects.
In our example, the single initializing function
Filesystem_proxy_init
will be generated:void Filesystem_proxy_init(struct Filesystem_proxy *self, struct nk_transport *transport, nk_iid_t iid) - Types that define the structure of the constant part of a message for each specific method.
In our example, two such types will be generated:
Filesystem_Open_req
(for a request) andFilesystem_Open_res
(for a response).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;
Methods and types used only on the server
- Type containing all endpoints of a component, and the initializing function. (For each server component.)
If there are embedded components, this type also contains their instances, and the initializing function takes their corresponding initialized structures. Therefore, if embedded components are present, their initialization must begin with the most deeply embedded component.
In our example, the
Operations_component
structure andOperations_component_init
function will be generated:typedef struct Operations_component { struct Filesystem *FS; }; void Operations_component_init(struct Operations_component *self, struct Filesystem *FS) - Type containing all endpoints provided directly by the server; all instances of components included in the server; and the initializing function.
In our example, the
Server_entity
structure andServer_entity_init
function will be generated:typedef struct Server_component { struct : Operations_component *OpsComp; } Server_component; void Server_entity_init(struct Server_entity *self, struct Operations_component *OpsComp) - Types that define the structure of the constant part of a message for any method of a specific interface.
In our example, two such types will be generated:
Filesystem_req
(for a request) andFilesystem_res
(for a response).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; }; - Types that define the structure of the constant part of a message for any method of any endpoint of a specific component.
If embedded components are present, these types also contain structures of the constant part of a message for any method of any endpoint included in all embedded components.
In our example, two such types will be generated:
Operations_component_req
(for a request) andOperations_component_res
(for a response).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; - Types that define the structure of the constant part of a message for any method of any endpoint of a specific component whose instance is included in the server.
If embedded components are present, these types also contain structures of the constant part of a message for any method of any endpoint included in all embedded components.
In our example, two such types will be generated:
Server_entity_req
(for a request) andServer_entity_res
(for a response).typedef union Server_component_req { struct nk_message base_; Filesystem_req OpsComp_FS; } Server_component_req; typedef union Server_component_res { struct nk_message base_; Filesystem_res OpsComp_FS; } Server_component_res; - Dispatch methods (dispatchers) for a separate interface, component, or process class.
Dispatchers analyze the received query (the RIID and MID values), call the implementation of the corresponding method, and then save the response in the buffer. In our example, three dispatchers will be generated:
Filesystem_interface_dispatch
,Operations_component_dispatch
, andServer_entity_dispatch
.The process class dispatcher handles the request and calls the methods implemented by this class. If the request contains an incorrect RIID (for example, an RIID for a different endpoint that this process class does not have) or an incorrect MID, the dispatcher returns
NK_EOK
orNK_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)In special cases, you can use dispatchers of the interface and the component. They take an additional argument: interface implementation ID (
nk_iid_t
). The request will be handled only if the passed argument and RIID from the request match, and if the MID is correct. Otherwise, the dispatchers returnNK_EOK
orNK_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)
Working with an IPC message arena
Arena overview
From the perspective of a developer of KasperskyOS-based solutions, an IPC message arena is a byte buffer in the memory of a process intended for storing variable-size data transmitted over IPC. This variable-size data includes input parameters, output parameters, and error parameters of interface methods (and/or elements of these parameters) that have variable-size IDL types. An arena is also used when querying the Kaspersky Security Module to store input parameters of security interface methods (and/or elements of these parameters) that have variable-size IDL types. (Parameters of fixed-size interface methods are stored in the constant part of an IPC message.) Arenas are used on the client side and on the server side. One arena is intended either for transmitting or for receiving variable-size data through IPC, but not for both transmitting and receiving this data at the same time. In other words, arenas can be divided into IPC request arenas (containing input parameters of interface methods) and IPC response arenas (containing output parameters and error parameters of interface methods).
Only the utilized part of an arena that is occupied by data is transmitted over IPC. (If it has no data, the arena is not transmitted.) The utilized part of an arena includes one or more segments. One segment of an arena stores an array of same-type objects, such as an array of single-byte objects or an array of structures. Arrays of different types of objects may be stored in different segments of an arena. The starting address of an arena must be equal to the boundary of a 2^N-byte sequence, where 2^N is a value that is greater than or equal to the size of the largest primitive type in the arena (for example, the largest field of a primitive type in a structure). The address of an arena chunk must also be equal to the boundary of a 2^N-byte sequence, where 2^N is a value that is greater than or equal to the size of the largest primitive type in the arena chunk.
An arena must have multiple segments if the interface method has multiple variable-size input, output, or error parameters, or if multiple elements of input, output, or error parameters of the interface method have a variable size. For example, if an interface method has an input parameter of the sequence
IDL type and an input parameter of the bytes
IDL type, the IPC request arena will have at least two segments. In this case, it may even require additional segments if a parameter of the sequence
IDL type consists of elements of a variable-size IDL type (for example, if elements of a sequence are string
buffers). As another example, if an interface method has one output parameter of the struct
IDL type that contains two fields of the bytes
and string
type, the IPC response arena will have two segments.
Due to the alignment of arena chunk addresses, there may be unused intervals between these chunks. Therefore, the size of the utilized part of an arena may exceed the size of the data it contains.
API for working with an arena
The set of functions and macros for working with an arena is defined in the header file sysroot-*-kos/include/nk/arena.h
from the KasperskyOS SDK. In addition, the function for copying a string to an arena is declared in the header file sysroot-*-kos/include/coresrv/nk/transport-kos.h
from the KasperskyOS SDK.
Information on the functions and macros defined in the header file sysroot-*-kos/include/nk/arena.h
is provided in the table below. In these functions and macros, an arena and arena chunk are identified by an arena descriptor (the nk_arena
type) and an arena chunk descriptor (the nk_ptr_t
type), respectively. An arena descriptor is a structure containing three pointers: one pointer to the start of the arena, one pointer to the start of the unused part of the arena, and one pointer to the end of the arena. An arena chunk descriptor is a structure containing the offset of the arena chunk in bytes (relative to the start of the arena) and the size of the arena chunk in bytes. (The arena chunk descriptor type is defined in the header file sysroot-*-kos/include/nk/types.h
from the KasperskyOS SDK.)
Creating an arena
To pass variable-size parameters of interface methods over IPC, you must create arenas on the client side and on the server side. (When IPC requests are handled on the server side using the NkKosDoDispatch()
function defined in the header file sysroot-*-kos/include/coresrv/nk/transport-kos-dispatch.h
from the KasperskyOS SDK, the IPC request arena and IPC response arena are created automatically.)
To create an arena, you must create a buffer (in the stack or heap) and initialize the arena descriptor.
The address of the buffer must be aligned to comply with the maximum size of a primitive type that can be put into this buffer. The address of a dynamically created buffer usually has adequate alignment to hold the maximum amount of data of the primitive type. To ensure the required alignment of the address of a statically created buffer, you can use the alignas
specifier.
To initialize an arena descriptor using the pointer to an already created buffer, you must use an API function or macro:
NK_ARENA_INITIALIZER()
macronk_arena_init()
functionnk_arena_create()
functionNK_ARENA_FINAL()
macronk_arena_init_final()
macro
The type of pointer makes no difference because this pointer is converted into a pointer to a single-byte object in the code of API functions and macros.
The NK_ARENA_INITIALIZER()
macro and the nk_arena_init()
and nk_arena_create()
functions initialize arena descriptor that may contain one or more segments. The NK_ARENA_FINAL()
and nk_arena_init_final()
macros initialize arena descriptor that contains only one segment spanning the entire arena throughout its entire life cycle.
To create a buffer in the stack and initialize the handle in one step, use the NK_ARENA_AUTO()
macro. This macro creates an arena that may contain one or more segments, and the address of the buffer created by this macro has adequate alignment to hold the maximum amount of data of the primitive type.
The size of an arena must be sufficient to hold variable-size parameters for IPC requests or IPC responses of one interface method or a set of interface methods corresponding to one interface, component, or process class while accounting for the alignment of segment addresses. Automatically generated transport code (the header files *.idl.h
, *.cdl.h
, and *.edl.h
) contain *_arena_size
constants whose values are guaranteed to comply with sufficient sizes of arenas in bytes.
The header files *.idl.h
, *.cdl.h
, and *.edl.h
contain the following *_arena_size
constants:
<interface name>_<interface method name>_req_arena_size
– size of an IPC request arena for the specified interface method of the specified interface<interface name>_<interface method name>_res_arena_size
– size of an IPC response arena for the specified interface method of the specified interface<interface name>_req_arena_size
– size of an IPC request arena for any interface method of the specified interface<interface name>_res_arena_size
– size of an IPC response arena for any interface method of the specified interface
The header files *.cdl.h
and *.edl.h
also contain the following *_arena_size
constants:
<component name>_component_req_arena_size
– size of an IPC request arena for any interface method of the specified component<component name>_component_res_arena_size
– size of an IPC response arena for any interface method of the specified component
The *.edl.h
header files also contain the following *_arena_size
constants:
<process class name>_entity_req_arena_size
– size of an IPC request arena for any interface method of the specified process class<process class name>_entity_res_arena_size
– size of an IPC response arena for any interface method of the specified process class
Constants containing the size of an IPC request arena or IPC response arena for one interface method (<interface name>_<interface method name>_req_arena_size
and <interface name>_<interface method name>_res_arena_size
) are intended for use on the client side. All other constants can be used on the client side and on the server side.
Examples of creating an arena:
Adding data to an arena before transmission over IPC
Before transmitting an IPC request on the client side or an IPC response on the server side, data must be added to the arena. If the NK_ARENA_FINAL()
macro or the nk_arena_init_final()
macro is used to create an arena, you do not need to reserve an arena chunk. Instead, you only need to add data to this chunk. If the NK_ARENA_INITIALIZER()
or NK_ARENA_AUTO()
macro, or the nk_arena_init()
or nk_arena_create()
function is used to create an arena, one or multiple segments in the arena must be reserved to hold data. To reserve an arena chunk, you must use an API function or macro:
__nk_arena_alloc()
functionnk_arena_store()
macro__nk_arena_store()
functionnk_arena_alloc()
macroNkKosCopyStringToArena()
function
The arena chunk descriptor, which is passed through the output parameter of these functions and macros and through the output parameter of the NK_ARENA_FINAL()
and nk_arena_init_final()
macros, must be put into the constant part or into the arena of an IPC message. If an interface method has a variable-size parameter, the constant part of IPC messages contains arena chunk descriptor containing the parameter instead of the actual parameter. If an interface method has a fixed-size parameter with variable-size elements, the constant part of IPC messages contains arena chunk descriptors containing the parameter elements instead of the actual parameter elements. If an interface method has a variable-size parameter containing variable-size elements, the constant part of IPC messages contains arena chunk descriptor containing the descriptors of other arena chunks that contain these parameter elements.
The nk_arena_store()
macro and the __nk_arena_store()
and NkKosCopyStringToArena()
functions not only reserve an arena chunk, but also copy data to this chunk.
The nk_arena_alloc()
macro gets the address of a reserved arena chunk. An arena chunk address can also be received by using the __nk_arena_get()
function or the nk_arena_get()
macro, which additionally pass the arena size through the output parameter.
A reserved arena chunk can be reduced. To do so, use the nk_arena_shrink()
macro or the _nk_arena_shrink()
function.
To undo a current reservation of arena chunks so that new chunks can be reserved for other data (after sending an IPC message), call the nk_arena_reset()
function. If the NK_ARENA_FINAL()
macro or nk_arena_init_final()
macro is used to create an arena, you do not need to undo a segment reservation because the arena contains one segment spanning the entire arena throughout its entire life cycle.
Examples of adding data to an arena:
Retrieving data from an arena after receipt over IPC
Prior to receiving an IPC request on the server side or an IPC response on the client side for an arena that will store the data received over IPC, you must undo the current reservation of segments by calling the nk_arena_reset()
function. This must be done even if the NK_ARENA_FINAL()
macro or the nk_arena_init_final()
macro is used to create the arena. (The NK_ARENA_INITIALIZER()
and NK_ARENA_AUTO()
macros, and the nk_arena_init()
and nk_arena_create()
functions create an arena without reserved segments. You do not need to call the nk_arena_reset()
function if this arena will only be used once to save data received over IPC.)
To receive pointers to arena chunks and the sizes of these chunks, you must use the __nk_arena_get()
function or the nk_arena_get()
macro while using the input parameter to pass the corresponding arena chunk descriptors received from the constant part and arena of the IPC message.
Example of receiving data from an arena:
Additional capabilities of the API
To get the arena size, call the nk_arena_capacity()
function.
To get the size of the utilized part of the arena, call the nk_arena_allocated_size()
function.
To verify that the arena chunk descriptor is correct, use the nk_arena_validate()
macro or the __nk_arena_validate()
function.
Information about API functions and macros
Functions and macros of arena.h
Function/Macro |
Information about the function/macro |
---|---|
|
Purpose Initializes the arena descriptor. Parameters
Macro values Arena descriptor initialization code. |
|
Purpose Initializes the arena descriptor. Parameters
Returned values N/A |
|
Purpose Creates and initializes the arena descriptor. Parameters
Returned values Arena descriptor. |
|
Purpose Creates a buffer in the stack, and creates and initializes the arena descriptor. Parameters
Macro values Arena descriptor. |
|
Purpose Initializes the arena descriptor containing only one segment. Parameters
Macro values Arena descriptor. |
|
Purpose Resets the reservation of arena chunks. Parameters
Returned values N/A |
|
Purpose Reserves an arena chunk with a specific size and a specific alignment. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the size of an arena. Parameters
Returned values Size of the arena in bytes. Additional information If the parameter has the |
|
Purpose Verifies that the arena chunk descriptor is correct. Parameters
Macro values It has a value of
It has a value of
It has a value of |
|
Purpose Verifies that the arena chunk descriptor is correct. Parameters
Returned values Returns
Returns
Returns |
|
Purpose Gets the pointer to the arena chunk and the size of this chunk. Parameters
Returned values Pointer to the arena chunk or |
|
Purpose Gets the size of the utilized part of the arena. Parameters
Returned values Size of the utilized part of the arena, in bytes. Additional information If the parameter has the |
|
Purpose Reserves an arena chunk for the specified number of objects of the defined type and copies these objects to the reserved chunk. Parameters
Macro values It has a value of |
|
Purpose Reserves an arena chunk with a defined alignment for data of a specific size and copies this data to the reserved segment. Parameters
Returned values Returns |
|
Purpose Initializes the arena descriptor containing only one segment. Parameters
Macro values N/A |
|
Purpose Reserves an arena chunk for the defined number of objects of the specified type. Parameters
Macro values It has the address of the reserved arena chunk if successful, otherwise |
|
Purpose Gets the address of an arena chunk and the number of objects of the specified type that can be put into this chunk. Parameters
Macro values It has the address of the arena chunk if successful, otherwise Additional information If the size of the arena chunk is not a multiple of the size of the type of objects for which this chunk is intended, it has the |
|
Purpose Reduces the size of an arena chunk. Parameters
Macro values It has the address of the reduced arena chunk if successful, otherwise Additional information If the required size of the arena chunk exceeds the current size, it has the If the alignment of the arena chunk that needs to be reduced does not match the type of objects for which the reduced chunk is intended, it has the If the arena chunk that needs to be reduced is the last chunk in the arena, the freed part of this chunk will become available for reservation of subsequent chunks. |
|
Purpose Reduces the size of an arena chunk. Parameters
Returned values Returns the address of the reduced arena chunk if successful, otherwise returns Additional information If the required size of the arena chunk exceeds the current size, it returns If the alignment of the arena chunk that needs to be reduced does not match the specified alignment, it returns If the arena chunk that needs to be reduced is the last chunk in the arena, the freed part of this chunk will become available for reservation of subsequent chunks. |
Transport code in C++
Before reading this section, you should review the information on the IPC mechanism in KasperskyOS and the IDL, CDL, and EDL descriptions.
Implementation of interprocess interaction requires transport code, which is responsible for generating, sending, receiving, and processing IPC messages.
However, a developer of a KasperskyOS-based solution does not have to write their own transport code. Instead, you can use the special tools and libraries included in the KasperskyOS SDK. These libraries enable a solution component developer to generate transport code based on IDL, CDL and EDL descriptions related to this component.
Transport code
The KasperskyOS SDK includes the nkppmeta
compiler for generating transport code in C++.
The nkppmeta
compiler lets you generate transport C++ proxy objects and stubs for use by both a client and a server.
Proxy objects are used by the client to pack the parameters of the called method into an IPC request, execute the IPC request, and unpack the IPC response.
Stubs are used by the server to unpack the parameters from the IPC request, dispatch the call to the appropriate method implementation, and pack the IPC response.
Generating transport code for development in C++
The CMake
commands add_nk_idl(), add_nk_cdl() and add_nk_edl() are used to generate transport proxy objects and stubs using the nkppmeta
compiler when building a solution.
С++ types in the *.idl.cpp.h file
Each interface is defined in an IDL description. This description defines the interface name, signatures of interface methods, and data types for the parameters of interface methods.
The CMake
command add_nk_idl() is used to generate transport code when building a solution. This command creates a CMake
target for generating header files for the defined IDL file when using the nkppmeta
compiler.
The generated header files contain a C++ representation for the interface and data types described in the IDL file, and the methods required for use of proxy objects and stubs.
The mapping of data types declared in an IDL file to C++ types are presented in the table below.
Mapping IDL types to C++ types
IDL type |
C++ type |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Working with transport code in C++
The scenarios for developing a client and server that exchange IPC messages are presented in the sections titled Statically creating IPC channels for C++ development and Dynamically creating IPC channels for C++ development.
Page topStatically creating IPC channels for C++ development
To implement a client program that calls a method of an endpoint provided by a server program:
- Include the generated header file (
*.edl.cpp.h
) of the client program description. - Include the generated header files of the descriptions of the utilized interfaces (
*.idl.cpp.h
). - Include the header files:
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/application.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/api.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/connect_static_channel.h
- Initialize the application object by calling the
kosipc::MakeApplicationAutodetect()
function. (You can also use thekosipc::MakeApplication()
andkosipc::MakeApplicationPureClient()
functions.) - Get the client IPC handle of the channel and the endpoint ID (
riid
) by calling thekosipc::ConnectStaticChannel()
function.This function gets the name of the IPC channel (from the init.yaml file) and the qualified name of the endpoint (from the CDL and EDL descriptions of the solution component).
- Initialize the proxy object for the utilized endpoint by calling the
MakeProxy()
function.
Example
To implement a server program that provides endpoints to other programs:
- Include the generated header file
*.edl.cpp.h
containing a description of the component structure of the program, including all provided endpoints. - Include the header files:
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/event_loop.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/api.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/serve_static_channel.h
- Create classes containing the implementations of interfaces that this program and its components provide as endpoints.
- Initialize the application object by calling the
kosipc::MakeApplicationAutodetect()
function. - Initialize the
kosipc::components::Root
structure, which describes the component structure of the program and describes the interfaces of all endpoints provided by the program. - Bind fields of the
kosipc::components::Root
structure to the objects that implement the corresponding endpoints.Fields of the
Root
structure replicate the hierarchy of components and endpoints that are collectively defined by the CDL and EDL files. - Get the server IPC handle of the channel by calling the
ServeStaticChannel()
function.This function gets the name of the IPC channel (from the init.yaml file) and the structure created at step 5.
- Create the
kosipc::EventLoop
object by calling theMakeEventLoop()
function. - Start the loop for dispatching incoming IPC messages by calling the
Run()
method of thekosipc::EventLoop
object.
Example
Dynamically creating IPC channels for C++ development
Dynamic creation of an IPC channel on the client side includes the following steps:
- Include the generated description header file (
*.edl.cpp.h
) in the client program. - Include the generated header files of the descriptions of the utilized interfaces (
*.idl.cpp.h
). - Include the header files:
/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
- Get the pointers to the server name and the qualified name of the endpoint by using a name server, which is a special kernel service provided by the
NameServer
program. To do so, you must connect to the name server by calling theNsCreate()
function and find the server that provides the required endpoint by using theNsEnumServices()
function. For more details, refer to Dynamically creating IPC channels (cm_api.h, ns_api.h). - Create an application object by calling the
kosipc::MakeApplicationAutodetect()
function. (You can also use thekosipc::MakeApplication()
andkosipc::MakeApplicationPureClient()
functions.) - Create a proxy object for the required endpoint by calling the
MakeProxy()
function. Use thekosipc::ConnectDynamicChannel()
function call as the input parameter of theMakeProxy()
function. Pass the pointers for the server name and qualified name of the endpoint obtained at step 4 to thekosipc::ConnectDynamicChannel()
function.
After successful initialization of the proxy object, the client can call methods of the required endpoint.
Example
Dynamic creation of an IPC channel on the server side includes the following steps:
- Include the generated header file (
*.edl.cpp.h
) containing a description of the component structure of the server, including all provided endpoints, in the server program. - Include the header files:
/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
- Create classes containing the implementations of interfaces that the server provides as endpoints. Create and initialize the objects of these classes.
- Create an application object by calling the
kosipc::MakeApplicationAutodetect()
function. - Create and initialize the
kosipc::components::Root
class object that describes the structure of components and endpoints of the server. This structure is generated from the descriptions in the CDL and EDL files. - Bind the
kosipc::components::Root
class object to the class objects created at step 3. - Create and initialize the
kosipc::EventLoop
class object that implements a loop for dispatching incoming IPC messages by calling theMakeEventLoop()
function. Use theServeDynamicChannel()
function call as an input parameter of theMakeEventLoop()
function. Pass thekosipc::components::Root
class object created at step 5 to theServeDynamicChannel()
function. - Start the loop for dispatching incoming IPC messages in a separate thread by calling the
Run()
method of thekosipc::EventLoop
object. - Create and initialize the object that implements the handler for receiving incoming requests to dynamically create an IPC channel.
When creating an object, you can use the
kosipc::SimpleConnectionAcceptor
class, which is the standard implementation of thekosipc::IConnectionAcceptor
interface. (Thekosipc::IConnectionAcceptor
interface is defined in the file named/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/connection_acceptor.h
.) In this case, the handler will implement the following logic: if the endpoint requested by the client was published on the server, the request from the client will be accepted. Otherwise, it will be rejected.If you need to create your own handler, you should implement your own request handling logic in the
OnConnectionRequest()
method inherited from thekosipc::IConnectionAcceptor
interface. This method will be called by the server when it receives a request for dynamic IPC channel creation from the client. - Create a
kosipc::EventLoop
class object that implements a loop for receiving incoming requests to dynamically create an IPC channel by calling theMakeEventLoop()
function. Use theServeConnectionRequests()
function call as an input parameter of theMakeEventLoop()
function. Pass the object created at step 9 to theServeConnectionRequests()
function.There can only be one loop for receiving incoming requests to dynamically create an IPC channel. The loop must work in one thread. The loop for receiving incoming requests to dynamically create an IPC channel must be created after the loop for dispatching incoming IPC channels is created (see step 7).
- Start the loop for receiving incoming requests for a dynamic connection in the current thread by calling the
Run()
method of thekosipc::EventLoop
object.
Example
If necessary, you can create and initialize multiple kosipc::components::Root
class objects combined into a list of objects of the ServiceList
type using the AddServices()
method. For example, use of multiple objects enables you to separate components and endpoints of the server into groups or publish endpoints under different names.
Example