KasperskyOS Community Edition 1.2

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

SInt8

int8_t

SInt16

int16_t

SInt32

int32_t

SInt64

int64_t

UInt8

uint8_t

UInt16

uint16_t

UInt32

uint32_t

UInt64

uint64_t

Handle

Handle (defined in coresrv/handle/handletype.h)

string

std::string

union

std::variant

struct

struct

array

std::array

sequence

std::vector

bytes

std::vector<std::byte>

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 top
[Topic cpp_proxy_stubs]

Statically creating IPC channels for C++ development

To implement a client program that calls a method of an endpoint provided by a server program:

  1. Include the generated header file (*.edl.cpp.h) of the client program description.
  2. Include the generated header files of the descriptions of the utilized interfaces (*.idl.cpp.h).
  3. 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
  4. Initialize the application object by calling the kosipc::MakeApplicationAutodetect() function. (You can also use the kosipc::MakeApplication() and kosipc::MakeApplicationPureClient() functions.)
  5. Get the client IPC handle of the channel and the endpoint ID (riid) by calling the kosipc::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).

  6. Initialize the proxy object for the utilized endpoint by calling the MakeProxy() function.

Example

// Create and initialize the application object kosipc::Application app = kosipc::MakeApplicationAutodetect(); // Create and initialize the proxy object auto proxy = app.MakeProxy<IDLInterface>( kosipc::ConnectStaticChannel(channelName, endpointName)) // Call the method of the required endpoint proxy->DoSomeWork();

To implement a server program that provides endpoints to other programs:

  1. Include the generated header file *.edl.cpp.h containing a description of the component structure of the program, including all provided endpoints.
  2. 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
  3. Create classes containing the implementations of interfaces that this program and its components provide as endpoints.
  4. Initialize the application object by calling the kosipc::MakeApplicationAutodetect() function.
  5. 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.
  6. 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.

  7. 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.

  8. Create the kosipc::EventLoop object by calling the MakeEventLoop() function.
  9. Start the loop for dispatching incoming IPC messages by calling the Run() method of the kosipc::EventLoop object.

Example

// Create class objects that implement interfaces // provided by the server as endpoints MyIDLInterfaceImp_1 impl_1; MyIDLInterfaceImp_2 impl_2; // Create and initialize the application object kosipc::Application app = kosipc::MakeApplicationAutodetect(); // Create and initialize the root object that describes // the components and endpoints of the server kosipc::components::Root root; // Bind the root object to the class objects that implement the server endpoints root.component1.endpoint1 = &impl_1; root.component2.endpoint2 = &impl_2; // Create and initialize the object that implements the // loop for dispatching incoming IPC messages kosipc::EventLoop loop = app.MakeEventLoop(ServeStaticChannel(channelName, root)); // Start the loop in the current thread loop.Run();
Page top
[Topic static_IPC_kosipc]

Dynamically creating IPC channels for C++ development

Dynamic creation of an IPC channel on the client side includes the following steps:

  1. Include the generated description header file (*.edl.cpp.h) in the client program.
  2. Include the generated header files of the descriptions of the utilized interfaces (*.idl.cpp.h).
  3. 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
  4. 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 the NsCreate() function and find the server that provides the required endpoint by using the NsEnumServices() function. For more details, refer to Dynamically creating IPC channels (cm_api.h, ns_api.h).
  5. Create an application object by calling the kosipc::MakeApplicationAutodetect() function. (You can also use the kosipc::MakeApplication() and kosipc::MakeApplicationPureClient() functions.)
  6. Create a proxy object for the required endpoint by calling the MakeProxy() function. Use the kosipc::ConnectDynamicChannel() function call as the input parameter of the MakeProxy() function. Pass the pointers for the server name and qualified name of the endpoint obtained at step 4 to the kosipc::ConnectDynamicChannel() function.

After successful initialization of the proxy object, the client can call methods of the required endpoint.

Example

NsHandle ns; // Connect to a name server Retcode rc = NsCreate(RTL_NULL, INFINITE_TIMEOUT, &ns); char serverName[kl_core_Types_UCoreStringSize]; char endpointName[kl_core_Types_UCoreStringSize]; // Get pointers to the server name and qualified name of the endpoint rc = NsEnumServices( ns, interfaceName, 0, serverName, kl_core_Types_UCoreStringSize, endpointName, kl_core_Types_UCoreStringSize); // Create and initialize the application object kosipc::Application app = kosipc::MakeApplicationAutodetect(); // Create and initialize the proxy object auto proxy = app.MakeProxy<IDLInterface>( kosipc::ConnectDynamicChannel(serverName, endpointName)) // Call the method of the required endpoint proxy->DoSomeWork();

Dynamic creation of an IPC channel on the server side includes the following steps:

  1. 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.
  2. 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
  3. Create classes containing the implementations of interfaces that the server provides as endpoints. Create and initialize the objects of these classes.
  4. Create an application object by calling the kosipc::MakeApplicationAutodetect() function.
  5. 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.
  6. Bind the kosipc::components::Root class object to the class objects created at step 3.
  7. Create and initialize the kosipc::EventLoop class object that implements a loop for dispatching incoming IPC messages by calling the MakeEventLoop() function. Use the ServeDynamicChannel() function call as an input parameter of the MakeEventLoop() function. Pass the kosipc::components::Root class object created at step 5 to the ServeDynamicChannel() function.
  8. Start the loop for dispatching incoming IPC messages in a separate thread by calling the Run() method of the kosipc::EventLoop object.
  9. 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 the kosipc::IConnectionAcceptor interface. (The kosipc::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 the kosipc::IConnectionAcceptor interface. This method will be called by the server when it receives a request for dynamic IPC channel creation from the client.

  10. Create a kosipc::EventLoop class object that implements a loop for receiving incoming requests to dynamically create an IPC channel by calling the MakeEventLoop() function. Use the ServeConnectionRequests() function call as an input parameter of the MakeEventLoop() function. Pass the object created at step 9 to the ServeConnectionRequests() 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).

  11. Start the loop for receiving incoming requests for a dynamic connection in the current thread by calling the Run() method of the kosipc::EventLoop object.

Example

// Create class objects that implement interfaces // provided by the server as endpoints MyIDLInterfaceImp_1 impl_1; MyIDLInterfaceImp_2 impl_2; // Create and initialize the application object kosipc::Application app = kosipc::MakeApplicationAutodetect(); // Create and initialize the root object that describes // the components and endpoints of the server kosipc::components::Root root; // Bind the root object to the class objects that implement the server endpoints. // The fields of the root object repeat the description of components and endpoints // defined collectively by the CDL and EDL files. root.component1.endpoint1 = &impl_1; root.component2.endpoint2 = &impl_2; // Create and initialize the object that implements the // loop for dispatching incoming IPC messages kosipc::EventLoop loopDynamicChannel = app.MakeEventLoop(ServeDynamicChannel(root)); // Start the loop for dispatching incoming IPC messages in a separate thread std::thread dynChannelThread( [&loopDynamicChannel]() { loopDynamicChannel.Run(); } ); // Create an object that implements a standard handler for receiving incoming requests // to dynamically create an IPC channel kosipc::SimpleConnectionAcceptor acceptor(root); // Create an object that implements a loop for receiving incoming requests // to dynamically create an IPC channel kosipc::EventLoop loopDynamicChannel = app.MakeEventLoop(ServeConnectionRequests(&acceptor)); // Start the loop for receiving incoming requests to dynamically create an IPC channel in the current thread loopConnectionReq.Run();

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

// Create and initialize the group_1 object kosipc::components::Root group_1; group_1.component1.endpoint1 = &impl_1; group_1.component2.endpoint2 = &impl_2; // Create and initialize the group_2 object kosipc::components::Root group_2; group_2.component1.endpoint1 = &impl_3; group_2.component2.endpoint2 = &impl_4; // Create and initialize the group_3 object kosipc::components::Root group_3; group_3.component1.endoint1 = &impl_5; // Create a list of objects ServiceList endpoints; endpoints.AddServices(group_1); endpoints.AddServices(group_2); endpoints.AddServices(group_3.component1.endpoint1, "SomeCustomEndpointName"); // Create an object that implements the handler for receiving incoming requests // to dynamically create an IPC channel kosipc::SimpleConnectionAcceptor acceptor(std::move(endpoints)); // Create an object that implements a loop for receiving incoming requests // to dynamically create an IPC channel kosipc::EventLoop loopDynamicChannel = app.MakeEventLoop(ServeConnectionRequests(&acceptor));
Page top
[Topic dynamic_IPC_kosipc]