# 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 show the different approaches that can be used to design application architecture. **NOTE:** This tutorial assumes that you've 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, you can build upon it with 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 aren't protected from each other. This tutorial shows how to move each component to their own process and define an API to facilitate communication over IPC. It also shows how to move each component into its 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's 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. It's encouraged 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, you need to create a second component that takes the string from the first component and log it to the syslog. 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 Use the created `printClient` and `printServer` components 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 there are two components, you need to make them talk to each other within the executable. To do this, you 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. For `printClient` to be able to use the `printer_Print` function in the `printServer`, add `LE_SHARED` to the function prototype. This adds the function name to the component's exported symbol table and allows `printClient` to 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 you 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 isn't in one of these directories it's considered not found, and the build will fail. The `$CURDIR` in the directory path is a build variable. It's 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 Now, 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 through a C header file can severely limit the reusability of components and not be friendly to developer skillsets. To solve this issue change the 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's 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. The source code can be found in the server.c file. The `printer_Print()` function can be called when a client binds to the printer service calls the API function printer\_Print(). The format of generated function names is: *<interface-name>\_<api-function-name>* The <interface-name> 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. It's implemented that way as it follows the server and client paradigm used 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, the `printServer` component provides an API and the `printClient` component using that API. It's time to create the app and have them start communicating. 7. Create the app. At this point create the .adef file and bundle the two components into one app. This is similar to what you did before except that you 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. This is so that it knows that it's 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 using 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 you 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: 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, you can set up `printClient` and `printServer` as two separate apps, and have them communicated only through defined APIs. This is the recommended way to create applications within the application framework. In this part of the tutorial you only need to make small changes to the components created in the earlier 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 you need to add an extern section. The extern section publishes the API as public and lets 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 you already bound the app to this external interface 4. Install both apps. Now you're ready to build the applications and install them on the target as you did before. 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: Aug 06, 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)