Use IPC

Lets explore how to use inter-process communication (IPC) between components:

  • Define a simple interface protocol that clients can use to ask servers to "Greet".
  • Create a server that prints "Hello world" whenever a client asks it to "Greet",
  • Create a client that asks the server to "Greet",

Here's what it looks like at runtime:

HelloIPC.png

When we show the static view of components inside executables, it looks like this:

HelloIPCWithComponents.png

Note that each interface has

  • an interface protocol
  • a friendly name.

In our example, the client and server both use the same friendly name "hello" for their interfaces, and must use the same protocol (or they wouldn't be able to communicate with each other!).

Define Interface Protocol

First, create a directory for our little project and change directories:

$ mkdir helloIPC
$ cd helloIPC

Next, define the interface protocol to use between the client and the server.

To create our greet function-call API, we create a definition file called "greet.api".

$ gedit greet.api

Add this inside greet.api:

FUNCTION Greet
(
);

This will declare a function called Greet that takes no arguments and returns nothing.

Create Server Component

Next, create a server component called greetServer that offers a greeting service called "hello" to be accessed using our simple "greet" API. Do this by creating a file called "Component.cdef" in a new directory called "greetServer".

$ mkdir greetServer
$ gedit greetServer/Component.cdef
Note
Keeping components in separate directories from each other is a requirement of the framework.

greetServer/Component.cdef should contain the following:

provides:
{
api:
{
hello = greet.api
}
}
 
sources:
{
greetServer.c
}

This declares that the component named greetServer (the name of the directory is the name of the component) provides a service called hello accessed using the API defined in greet.api where the source code can be found in the file greetServer.c.

Note
The source code file name extension is used to identify the programming language it's written in.

To implement the server (in C), create a file greetServer/greetServer.c:

$ gedit greetServer/greetServer.c

Make it contain the following:

#include "legato.h"
#include "interfaces.h"
 
void hello_Greet(void)
{
LE_INFO("Hello world.");
}
 
{
 
}

The file interfaces.h is auto-generated based on the contents of greet.api and greetServer's Component.cdef. It'll contain a prototype of the function hello_Greet(), which we implemented in greetServer.c.

The function hello_Greet() will be called when a client binds to our hello service and calls the function Greet(). The name hello_Greet follows this pattern:

<interface-name> '_' <api-function-name>

The <interface-name> is the name given to the provided interface. In our example, it's the name hello before the = in the line hello = greet.api.

Note
If you forget to implement a service function on the server or if you give it the wrong name, the link stage will fail and complain that the symbol is unresolved. You'll know you missed a function, and you'll be able to see what the correct name of the function should be.

Create Client Component

Now that we have a server, let's create a client to use its service.

First, create a directory for the greetClient component and a file in it called Component.cdef:

$ mkdir greetClient
$ gedit greetClient/Component.cdef

greetClient/Component.cdef should contain:

requires:
{
api:
{
hello = greet.api
}
}
 
sources:
{
greetClient.c
}

To implement the client using C, create a file called greetClient/greetClient.c:

$ gedit greetClient/greetClient.c

Make it contain the following:

#include "legato.h"
#include "interfaces.h"
 
{
hello_Greet();
}

The interfaces.h file the client includes is different than the one the server uses. This client one is specially generated based on the contents of greetClient/Component.cdef and greet.api.

In the client, we use the component initializer to call hello_Greet() at start-up.

You can add a message to the COMPONENT_INIT so the logs will show the client calling the server:

{
LE_INFO("greetClient is calling hello_Greet \n");
hello_Greet();
}

Declare IPC Availability

You can create an Application Definition .adef file with an extern section to declare the app is available for binding:

extern:
{
helloWorld.greetServer.greet.api
}

You only need to do this if you want to leave your interface unbound, and bind it later in the .sdef file bindings section.