# Extend hello world

Source: [https://docs.qualcomm.com/doc/80-41102-1/topic/extend_hello_world.html](https://docs.qualcomm.com/doc/80-41102-1/topic/extend_hello_world.html)

This tutorial breaks the helloWorld app into `printClient` and `printServer` components and uses these components to demonstrate the different approaches that can be used to design application architecture. **NOTE:** This tutorial assumes that you've previously completed the [helloWorld tutorial](https://docs.qualcomm.com/doc/80-41102-1/topic/hello_world.html).

Now that you’ve created your first helloWorld app, we’re going to build upon it and introduce some key TelAF concepts. The helloWorld app is a simple app that logs a string to the syslog. This tutorial extends first the helloWorld app which starts with two components existing within a single process. Both components have full access rights and communication to each other and processes are not protected from each other. In this tutorial we will move each component to their own process and define an API to facilitate communication over IPC. Lastly, we will move each component into it's own application and use an API to facilitate IPC communication. At the end, the two apps will only be able to communicate over IPC and will only have access to what is defined in the API.

Note: When designing your own components or applications it's important to find a configuration that allows the access your app needs while maintaining system security. We encourage the use of IPC and application sandboxing as they allow for secure communications between apps while protecting the internal components.

## Create components

To prepare for this tutorial, we need to create a second component that will take the string from the first component and log it to the syslog.

We are going to create two components: `printClient` and `printServer`.

1. Create the directories.

    Use `mkdir` to make the directories. Your directory structure should match the following.

        helloIPC/
        ├──printClient/
        └──printServer/Copy to clipboard
2. Create the `printServer` component.

    1. `cd` into the printServer directory and create your source file (server.c). The server.c file is going to take the passed parameter from the client and print it to the logs.
    2. Ensure server.c contains the following.

            #include "legato.h"
            #include "interfaces.h" 
            void printer_Print(const char* message)
            { 
                LE_INFO("******** Client says '%s'", message);
            }
            COMPONENT_INIT
            {
            }Copy to clipboard
    3. Create the Component.cdef file so the component is built correctly with mktools.

            sources:
            {
                server.c
            }Copy to clipboard
3. Create the `printClient` component.

    1. `cd` into the printClient directory and create a source file called client.c.

            #include "legato.h"
            #include "interfaces.h"
            COMPONENT_INIT
            { 
                LE_INFO("Asking server to print 'Hello, world!'"); 
                printer_Print("Hello, world!");
            }Copy to clipboard
    2. Create the Component.cdef file with the following contents.

            sources:
            { 
                client.c
            }Copy to clipboard

    Now we have the `printClient` and `printServer` components created, we will use this as a basis for the rest of the tutorial.

    Your directory for helloIPC should match the following.

        helloIPC/
        ├── printClient/
        │ ├── client.c
        │ └── Component.cdef
        └── printServer/ 
         ├── Component.cdef 
         └── server.cCopy to clipboard

    Now we have two components, next we need to make them talk to each other within the executable. To do this, we need a standard header file in the `printServer` component which can be exposed outside of itself.
4. Create the `printServer` header.

    1. Create the header file.

            $vim server.hCopy to clipboard
    2. Update the file contents to match the following code.

            #ifndef SERVER_H_INC
            #define SERVER_H_INC 
            LE_SHARED void printer_Print(const char* message); 
            #endifCopy to clipboard
    3. Add `#include "server.h"` in both the client.c and server.c files.

Note: By default all symbols are hidden in the build process, to export symbols you need to explicitly define them in the header. Because we want `printClient` to be able to use the `printer_Print` function in the `printServer` we need to add `LE_SHARED` to the function prototype so that the function name is added to the component's exported symbol table and `printClient` can use the function.

            #include "legato.h"
            #include "interfaces.h"
            #include "server.h"Copy to clipboard
    4. Add a cflags section in the printClient component's Component.cdef file.

        This tells the build tools where to find the server.h header file. The server component (printServer) will be able to find this file automatically. This is because the header file is in the same directory as the source file (server.c). However, in the client component's code the .h file is located in a different directory than the client.c.

            cflags:
            { 
                -I $CURDIR/../printServer
            }Copy to clipboard

        The `-I` tells the compiler we want to add a new directory to the search path. When you use the` #include` directive you tell the compiler to search for that file. The compiler uses a list of directories called a search path to perform this search. If the file in question is not in one of these directories it is considered not found, and the build will fail.

        The `$CURDIR` in the directory path is a build variable. It is automatically set by the mktools. Inside of a Component.cdef the variable `$CURDIR` automatically points to the directory that the Component.cdef file was found.

## Executable with 2 components

We are now ready to create an executable with both components, bundle the executable into an app, and install it on the target.

1. Create the helloApp.adef file.

        $ vim helloApp.adefCopy to clipboard
2. Add the following contents.

        executables:
        { 
            helloPrint = ( printClient printServer )
        }
        processes:
        { 
            run: 
            { 
                ( helloPrint ) 
            }
        }Copy to clipboard

    The two components are added to the same `helloPrint` executable. `helloPrint` is then given a process to run in and bundled into the app.

    Your directory structure for helloIPC should match the following.

        helloIPC/
        ├── helloApp.adef
        ├── printClient/
        │ ├── client.c
        │ └── Component.cdef
        └── printServer/
         ├── Component.cdef
         ├── server.c
         └── server.hCopy to clipboard
3. Create the app.

    1. Source the TelAF app environment setup script from the toolchain path.

            $ source /opt/qct/sa525m/environment-setup-telaf-appCopy to clipboard
    2. From the app directory, run `mkapp` to build the app package.

            $ mkapp -t sa525m -i $TELAF_INTERFACES  -i $TELAF_MNGD_INTERFACES helloApp.adef -C "-O2" -X "-O2"Copy to clipboard
4. Push the package to the target's `/data/` directory.

        $ adb push helloApp.sa525m.update /data/    #This is run on your PC with adb installed.Copy to clipboard
5. Install the package from the target's /data/ directory.

        $ update helloApp.sa525m.updateCopy to clipboard
6. Run `logread` on the target to see logs like the ones shown below.

        # logread | grep Hello
        Jan 17 18:06:02 sa525m user.info Legato: INFO | helloPrint[5642]/printClient T=main | client.c _printClient_COMPONENT_INIT() 7 | Asking server to print 'Hello, world!'
        Jan 17 18:06:02 sa525m user.info Legato: INFO | helloPrint[5642]/printServer T=main | server.c printer_Print() 7 | ******** Client says 'Hello, world!'Copy to clipboard

Note: From the log, you can see both components are running on the same PID in the `helloPrint` executable.

## 2 executables with each component having its own

Making two components share same the process and API exposure via a C header file can severely limit the reusability of components and not be friendly to developer skillsets. To solve this issue we modify our app with the components running in two separate processes. Each component runs in its own process, which allows the capability to restart each process individually.

1. Remove the server.h file and it’s references.

        $ rm printServer/server.hCopy to clipboard
2. Remove `#include server.h` from both printClient/client.c and printServer/server.c.
3. Remove the `cflags` section in the printClient/Component.cdef file as it's not needed anymore.
4. Create the API file under the root directory of the app.

        $ vim printer.apiCopy to clipboard

    The printer.api file contents should match the following.

        DEFINE MESSAGE_LEN = 100; 
        FUNCTION Print
        ( 
            string message[MESSAGE_LEN] IN     ///< Message to be printed into the log, maximum 100 characters.
        );Copy to clipboard
5. Add the following to printServer/Component.cdef before the `sources:` section.

    This tells the `printServer` component that it is providing the API.

        provides:
        { 
            api: 
            { 
                printer = printer.api 
            }
        }Copy to clipboard

    This declares that the component named `printServer` (the name of the directory is the name of the component) provides a service called printer accessed using the API defined in printer.api while the source code can be found in the server.c file.

    The `printer_Print()` function can be called when a client binds to our printer service calls the API function printer\_Print().

    The format of generated function names is:

    *&lt;interface-name&gt;\_&lt;api-function-name&gt;*

    The &lt;interface-name&gt; is the name given to the provided interface. In this tutorial, it's the name `printer` before the `=` in the line `printer = printer.api`. Printer in this case is optional and doesn't have to be the same name as the *.api* file. We chose to implement it that way as it follows the server and client paradigm we use for this tutorial.

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.
6. Use the API in `printClient`.

    Add the following to printClient/Component.cdef before the `sources:` section.

    This tells the client app that it requires the printer API to be able to print messages.

        requires:
        {
            api:
            {
                printer = printer.api
            }
        }Copy to clipboard

    Your directory structure should contain the following files now:

        helloIPC/
        ├── helloApp.adef
        ├── printClient/
        │ ├── client.c
        │ └── Component.cdef
        ├── printer.api
        └── printServer/
         ├── Component.cdef
         └── server.cCopy to clipboard

    Next, we have the `printServer` component providing an API and the `printClient` component using that API. It's time to create our app and have them start communicating.
7. Create the app.

    At this point we are going to create the .adef file and bundle the two components into one app. This is very similar to what we did before except that we will be defining two executables and two processes.

    Start by creating the .adef file.

        $ vim helloIPC1.adefCopy to clipboard

    Add the following into the helloIPC1.adef:

        executables:
        {
            helloClient = ( printClient )
            helloServer = ( printServer )
        }
        processes:
        {
            run:
            {
                ( helloClient )
                ( helloServer )
            }
        }
        bindings:
        {
            helloClient.printClient.printer -> helloServer.printServer.printer
        }Copy to clipboard

    The `bindings:` section is used to connect the client interface to the server interface so that the client can call the printer functions on the server. When the app is built it will generate the system information for the client so that it knows that it is allowed to use the print function in the server executable. Because both executables are within one app, they have unrestricted permissions to each other. mkapp checks that all client connections are satisfied.
8. Install the app.

    Source the TelAF app environment setup script from the toolchain path.

        $ source /opt/qct/sa525m/environment-setup-telaf-appCopy to clipboard
9. Compile the app from the app source directory.

        $ mkapp -t sa525m -i $TELAF_INTERFACES -i $TELAF_MNGD_INTERFACES helloIPC1.adef -C "-O2" -X "-O2"
        [9/9] Packaging app                            Copy to clipboard
10. Update the compiled app to the target via adb.
11. Run `logread `to see to see logs similar to the following.

        # logread | grep Hello
        Jan 18 17:10:53 sa525m user.info Legato: INFO | helloClient[29666]/printClient T=main | client.c _printClient_COMPONENT_INIT() 6 | Asking server to print 'Hello, world!'
        Jan 18 17:10:53 sa525m user.info Legato: INFO | helloServer[29669]/printServer T=main | server.c printer_Print() 6 | ******** Client says 'Hello, world!'Copy to clipboard

    From the log we can find the `printClient` and `printServer` components are running in their own processes independently. Each component has its own PID.

    This allows you to kill one process if needed and not the other. This also allows you to monitor specific processes or if your app has a critical process set up a watchdog monitor on it.

    The benefits of providing components their own interface are:

    - Process isolation – Each component is in the same sandbox but code is isolated.
    - Complexity of the app internals are hidden – The components can still communicate but are bundled as one app.

Depending on your security model you may want to separate your components into separate apps. Currently, the client and the server have access to the entire environment. If this server needs to keep files or resources protected from the client, there is no mechanism to do that.

## Migrate to 2 applications

To isolate the component’s running environment, we can set up `printClient` and `printServer` as two separate apps, and have them communicated only via defined APIs. This is the recommended way to create applications within the application framework.

In this portion of the tutorial we only need to make small changes to the components created in the previous steps. Most of the code is reusable.

1. Create helloClient.adef
2. Remove helloApp.adef and next, create helloClient.adef

        $ vim helloClient.adefCopy to clipboard

    helloClient.adef should contain the following sections:

        executables:
        {
            helloClient = ( printClient )
        }
        processes:
        {
            run:
            {
                ( helloClient )
            }
        }
        bindings:
        {
            helloClient.printClient.printer -> helloServer.printer
        }Copy to clipboard

    Binding the interfaces also sets up permissions so that you are explicitly allowing the client app to communicate with the server app

    Create helloServer.adef
3. Create a .adef for helloServer.

    In the helloServer.adef we need to add an extern section. The extern section publishes the API as public and will let other apps connect to it.

        $ vim helloServer.adefCopy to clipboard

    helloServer.adef should contain the following sections:

        executables:
        {
            helloServer = ( printServer )
        }
        processes:
        {
            run:
            {
                ( helloServer )
            }
        }
        extern:
        {
            printer = helloServer.printServer.printer
        }Copy to clipboard

    The extern section publishes the printer api external to itself so that other applications can use it. In the .adef for the helloClient app we already bound the app to this external interface
4. Install both apps.

    Now we’re ready to build the applications and install them on the target as we did previously.

    1. Source the TelAF app environment setup script from the toolchain path.

            $ source /opt/qct/sa525m/environment-setup-telaf-appCopy to clipboard
5. Compile the app from the app directory.

        $ mkapp -t sa525m -i $TELAF_INTERFACES -i $TELAF_MNGD_INTERFACES helloClient.adef -C "-O2" -X "-O2"
        $ mkapp -t sa525m -i $TELAF_INTERFACES -i $TELAF_MNGD_INTERFACES helloServer.adef -C "-O2" -X "-O2"Copy to clipboard
6. Install helloServer first and then helloClient
7. Check the logread output.

        # logread | grep Hello
        Jan 18 20:31:03 sa525m user.info Legato: INFO | helloClient[902]/printClient T=main | client.c _printClient_COMPONENT_INIT() 6 | Asking server to print 'Hello, world!'
        Jan 18 20:31:03 sa525m user.info Legato: INFO | helloServer[837]/printServer T=main | server.c printer_Print() 6 | ******** Client says 'Hello, world!' Copy to clipboard

**Parent Topic:** [Tutorials](https://docs.qualcomm.com/doc/80-41102-1/topic/tutorials.html)

Last Published: May 12, 2026

[Previous Topic
Hello world](https://docs.qualcomm.com/bundle/publicresource/80-41102-1/topics/hello_world.md) [Next Topic
Legacy C App using TelAF API](https://docs.qualcomm.com/bundle/publicresource/80-41102-1/topics/legacy_c_app_using_telaf_api.md)