Component Definition .cdef

Component.cdef files can contain these sections:

Assets

Used to describe collections of information (fields) that can be exchanged with cloud services like AirVantage.

The assets section can hold multiple assets, each asset is given a name.

An asset can be:

  • variables values that are readable by the server; writable by the client.
  • settings values that are writable by the server; readable by the client.
  • commands that are custom commands sent from the server and executable by the client.

The variable and setting fields can have an associated data type and optionally a default value. The current data types supported are bool, int, float, and string.

In this code sample, helloWorld app has one setting (the message to be logged), one variable (records how many times a message has been logged). The app also exposes two commands: one to log the currently configured message and one to log the default message.

The assets definition looks like this:

assets:
{
    message =
    {
        settings:
        {
            string greeterMessage = "Hello world."   // When requested log this message.
        }

        variables:
        {
            int greetCount     // How many times has a greeting been logged?
        }

        commands:
        {
            greetNow           // Log the message recorded in greeterMessage.
            standardGreeting   // Use "Hello world," instead of what is set in greeterMessage.
        }
    }
}

Once you've defined your assets, it's up to the app to instantiate instances of them at run-time using the AirVantage Data API. Also see Manage AirVantage Data.

Bundles

Lists additional files or directories to be copied from the build host into the app so they’re available to the app at runtime (e.g., audio files, web pages, executable scripts or programs built using some external build system).

bundles:
{
    file:
    {
        // Include the web server executable (built using some other build tool) in the app's /bin.
        [x] 3rdParty/webServer/bin/wwwServ  /bin/

        // Put the company logo into the app's /var/www/ for read-only access by the web server.
        images/abcCorpLogo.jpg  /var/www/

        // Make the appropriate welcome page for the product appear at /var/www/index.html.
        webContent/$PRODUCT_ID/welcome.html  /var/www/index.html

        // Create a file to record persistent custom audio messages into.
        [w] audio/defaultMessage.wav  /usr/share/sounds/customMessage.wav
    }

    dir:
    {
        // Recursively bundle the directory containing all the audio files into the app.
        // It will appear to the app read-only under /usr/share/sounds/.
        audio   /usr/share/sounds
    }
}

Three things need to be specified for each file or directory:

  • access permissions
  • build system path
  • target path

Access permissions - any combination of one or more of the following letters, enclosed in square brackets:

  • r = readable
  • w = writeable
  • x = executable

If permissions values are not specified, then read-only ([r]) is the default.

Directories always have executable permission set so they can be traversed. Setting the [x] permission in the dir: subsection causes the files under the directory to be made executable.

Setting [w] in the dir: subsection causes all files under that directory to be writeable, but the directory itself will not be writeable.

Note
Directories in the persistent (flash) file system are never made writeable because the on-target flash file system does not support usage quotas (yet).

Build system path - file system path on the build PC where the file is located at build time.

The path can be relative to the directory where the .adef file is located.

Note
Environment variables can be used inside these paths.

Target path - file system path on the target where the file will appear at runtime.

It's an absolute path inside the app's sandbox file system.

If the path ends with '/', it means the directory path where the source object (file or directory) will be copied. The destination object will have the same name as the source object.

If the path doesn't end in a '/', it's a full destination object path. The destination object could have a different name than the source object.

Note
If the app is running unsandboxed, the bundled files and directories can be found in their installation location under /legato/systems/current/apps/xxxx, where xxxx is replaced by the app name.

Quoting Paths

File paths can be enclosed in quotation marks (either single ' or double "). This is required when the file path contains spaces or comment start sequences

"//" or  "/*"

File Ownership and Set-UID Bits

When the app is installed on a target:

  • the owner and group are set to root on all files in the app.
  • the setuid bit is cleared on everything in the app.

C Flags

Provides a way to specify command-line arguments to pass to the compiler when compiling C source code files.

Flags are separated by whitespace.

cflags:
{
-g -O0
-DDEBUG=1
}

C++ Flags

Provides a way to specify command-line arguments to pass to the compiler when compiling C++ source code files.

Flags are separated by whitespace.

cxxflags:
{
-std=c++0x
-g -O0
}

Linker Flags

Provides a way to specify command-line arguments to pass to the compiler when linking C/C++ object (.o) files together into a component shared library (.so) file.

Flags are separated by whitespace.

ldflags:
{
-Lfoo/bar
}

Pools

Warning
This feature not yet implemented.

Specifies the number of memory pool blocks that each memory pool should contain.

pools:
{
myPool = 45
}

Provides

Lists things this component provides (exports) to other software either inside or outside of the app.

The only subsection supported today is the api subsection.

API

Lists IPC services provided by this component to other components.

Contents use the same syntax as the requires: API section, except the options are different.

Here's a code sample where greet.api defines a function called Send() where the C source code for the component (in greetServer.c) is implement a function called greet_Send().

provides:
{
api:
{
greet.api // We offer the Greet API to others so they can say “hello” to the world.
heat = digitalOutput.api
cool = digitalOutput.api
}
}
sources:
{
greetServer.c
tempControl.c
}

The component must implement the API functions being provided.

In C, the source code must #include “interfaces.h” to get the auto-generated function prototype definitions and type definitions. The function and type names defined in the .api files are prefixed with the interface name and an underscore (similar to required APIs).

[manual-start] Option

To reduce the initialization code a component writer needs to write, the build tools automatically try to advertise the service when the executable is run. Sometimes this is not the preferred behaviour.

The [manual-start] option tells the build tools not to automatically advertise this API with the Service Directory when the process starts. If [manual-start] option is used, the component can control when it wants to start offering the service to others by calling the xxxx_AdvertiseService() function explicitly in the component source code when it's ready.

provides:
{
api:
{
foo.api [manual-start]
}
}

[async] Option

The server of a service can also implement the functions as if they were called directly by the client (even though the client may be running inside another process). When the client calls an API function, the server's API function gets called, and when the server returns from the function, the function returns in the client process.

Sometimes the server needs to hold onto the client request and do other things (like handing requests from other clients in the meantime) before sending a response back. This is called asynchronous mode, and is enabled using the [async] keyword on the end of the api section entry:

provides:
{
api:
{
bar.api [async]
}
}

When asynchronous mode is enabled for a server-side interface, the generated code changes as follows:

  • commandRef parameter is added to the beginning of all the API functions' parameter lists.
  • return value is removed from every API function.
  • Respond() function is generated for every API function.

In async mode, the server responds to the client's call to API function F() by calling the associated FRespond() function.

The Respond functions all take the commandRef as their first parameter. If an API function has a return value, that return value is sent to the client through the second parameter of the Respond function. Any output parameters defined in the API function are also passed as parameters to the Respond function.

See API Files for more information, or try it and have a look at the generated header files.

Requires

The requires: section specifies things the component needs from its runtime environment.

It can contain various subsections.

API

Lists IPC APIs used by this component.

Here's a code sample of a component using the Configuration Data API (defined in le_cfg.api) to read its configuration data:

requires:
{
api:
{
le_cfg.api
}
}

This creates a client-side IPC interface called le_cfg on this component, and it makes the functions and data types defined inside le_cfg.api available for use in the component's program code.

The name of the .api file (minus the .api extension) is the name of the interface, and in C code, the names of functions and data types defined in the .api file are prefixed with the name of the interface with an underscore separator.

requires:
{
api:
{
print.api // WriteLine() from the API will appear in my C code as "print_WriteLine()".
}
}

To rename the interface, an interface name followed by an equals sign ('=') can be added in front of the .api file path.

requires:
{
api:
{
hello = greet.api // Send() from the API will appear as "hello_Send()" in my code.
}
}

Multiple instances of the same API listed in the api: section must have unique instance names, and appear as separate functions with different prefixes.

requires:
{
api:
{
heat = digitalOutput.api // Used to turn on and off the heater.
cool = digitalOutput.api // Used to turn on and off the cooling (A/C).
}
}

If digitalOutput.api defines two functions On() and Off(), the component’s source code would have four functions available to it: heat_On(), heat_Off(), cool_On(), and cool_Off().

C/C++ source code must #include “interfaces.h” to use the auto-generated function definitions. The build tools will automatically generate a version of interfaces.h customized for your component that includes all declarations for all the interfaces the component uses.

The build tools search for the interface definition (.api) file based on the interface search path.

Options

To reduce the amount of initialization code a component needs to write, the build tools automatically generate the client-side IPC code for that API, and automatically try to connect to the server when the executable is run. There are a couple of options that can be used to suppress this behaviour.

The [types-only] option tells the build tools the client only wants to use type definitions from the API. This means the client-side IPC code will not be generated for this API, but the types defined in the API will still be available to the component (through interfaces.h in C/C++).

The [manual-start] option tells the build tools not to automatically connect to this API's server when the process starts. This means the component can control when it wants to connect to the server by calling the xxxx_ConnectService() function explicitly in the component source code.

requires:
{
api:
{
foo.api [types-only] // Only need typedefs from here. Don't need IPC code generated.
bar.api [manual-start] // I'll start this when I'm ready by calling bar_ConnectService().
}
}

File

Declares:

  • specific files that reside on the target outside of the app, but made accessible to the app.
  • location inside the app's sandbox where the file will appear.

Things listed in requires are expected to be found on the target at runtime. They're not copied into the app at build time; they are made accessible to the app inside of its sandbox at runtime.

Each entry consists of two file system paths:

  • path to the object in the file system outside of the app, which must be an absolute path (beginning with ‘/’).
  • absolute file system path inside the app’s sandbox where the object will appear at runtime.

A file path can be enclosed in quotation marks (either single ' or double "). This is required when it contains spaces or character sequences that would start comments.

The first path can't end in a '/'.

If the second path ends in a '/', then it's specifying the directory where the object appears, and the object has the same name inside the sandbox as it has outside the sandbox.

requires:
{
    file:
    {
        // I get character stream input from outside via a named pipe (read-only)
        /var/run/someNamedPipe  /var/run/

        // I need to be able to play back audio files installed in /usr/local/share/audio.
        "/usr/local/share/audio/error message.wav" /usr/share/audio/
        '/usr/local/share/audio/success message.wav' /usr/share/audio/
    }
}
Note
Even though the file system object appears in the app's sandbox it still needs permissions settings on the file. File permissions (both DAC and MAC) and ownership (group and user) on the original file in the target system remain in effect inside the sandbox.

It's also possible to give the object a different names inside and outside of the sandbox by adding a name to the end of the second path.

requires:
{
    file:
    {
        // Program uses /var/run/someNamedPipe which it calls /var/run/externalPipe.
        /var/run/someNamedPipe  /var/run/externalPipe
    }
}
Warning
When something is accessible from inside an app sandbox, there are potential security risks (e.g., access to the object could be exploited by the app, or hacker, to access sensitive information or launch a denial-of-service attack on other apps within the target device or other devices connected to the target device).

Device

Declares:

  • device files that reside on the target outside of the app, but made accessible to the app.
  • location inside the app's sandbox where the file will appear.
  • access permissions the app is given to the device file.

Things listed in requires are expected to be found on the target at runtime. They're not copied into the app at build time; they are made accessible to the app inside of its sandbox at runtime.

Each entry consists of two file system paths and a set of optional access permissions:

  • access permissions, readable ([r]) and/or writeable ([w]). Executable is not allowed on device files. If permission values are not specified, then read-only ([r]) is the default.
  • path to the object in the file system outside of the app, which must be an absolute path (beginning with ‘/’). This must be a path to a valid character or block device file.
  • absolute file system path inside the app’s sandbox where the object will appear at runtime.

A file path can be enclosed in quotation marks (either single ' or double "). This is required when it contains spaces or character sequences that would start comments.

The first path can't end in a '/'.

If the second path ends in a '/', then it's specifying the directory where the object appears, and the object has the same name inside the sandbox as it has outside the sandbox.

requires:
{
    device:
    {
        // I get read-only access to the SPI port.
        [r]     /dev/sierra_spi   /dev/sierra_spi

        // I get read-only access to the NMEA port.
                /dev/nmea         /dev/nmea

        // I get read and write access to the I2C port.
        [rw]    /dev/sierra_i2c   /dev/
    }
}

Note that if a hot-plug device is unplugged and plugged back in, the app must be restarted before it can access the device.

It's also possible to give the object a different names inside and outside of the sandbox by adding a name to the end of the second path.

requires:
{
    device:
    {
        /dev/ttyS0  /dev/port1     // Program uses /dev/port1, but UART0 is called /dev/ttyS0.
    }
}
Warning
When something is accessible from inside an app sandbox, there are potential security risks (e.g., access to the object could be exploited by the app, or hacker, to access sensitive information or launch a denial-of-service attack on other apps within the target device or other devices connected to the target device).
This section is experimental. Future releases of may not support this section.

Dir

Specifies directories on target device to make accessible to the app.

The location inside the app's sandbox at which the directory will appear is also specified.

Things listed here are expected to be found on the target at runtime. They are not copied into the app at build time; they are made accessible to the app inside of its sandbox at runtime.

Each entry consists of two file system paths:

  • The first path is the path to the directory outside of the app. This must be an absolute path (beginning with ‘/’) and can never end in a '/'.
  • The second path is the absolute path inside the app’s sandbox where the directory will appear at runtime.

Paths can be enclosed in quotation marks (either single ' or double "). This is required when it contains spaces or character sequences that would start comments.

If the second path ends in a '/', then it's specifying the directory into which the object will appear, and the object will have the same name inside the sandbox as it has outside the sandbox.

requires:
{
    dir:
    {
        // I need access to /proc for debugging.
        /proc   /

        // For now, I want access to all executables and libraries in /bin and /lib.
        // Later I'll remove this and replace with just the files I really need in the field.
        // Also, I don't want to hide the stuff that the tools automatically bundle into my app's
        // /bin and /lib for me, so I'll make the root file system's /bin and /lib accessible as
        // my app's /usr/bin and /usr/lib.
        /bin    /usr/bin
        /lib    /usr/lib
    }
}
Note
Although the directory appears in the app's sandbox, it doesn't mean the app can access it. The directory permissions settings must also allow it. File permissions (both DAC and MAC) and ownership (group and user) on the original files in the target system remain in effect inside the sandbox.
Warning
Any time anything is accessible from inside an app sandbox, the security risks must be considered carefully. Ask yourself if access to the object can be exploited by the app (or a hacker who has broken into the app) to access sensitive information or launch a denial-of-service attack on other apps within the target device or other devices connected to the target device?
Note
It's not possible to put anything inside of a directory that was mapped into the app from outside of the sandbox. If you require /bin to appear at /usr/bin, you can't then bundle a file into /usr/bin or require something to appear in /usr/bin; that would have an effect on the contents of the /bin directory outside of the app.

Lib

The lib: subsection of the requires: section is used to specify that a shared (dynamic) library is required by any executable that the component is part of.

The required library will be linked with executables that the component is a part of.

Specifying a shared library file's path will result in "<c>-L<c>" and "<c>-l<c>" arguments being added to the linker's command line.

This is useful when linking to libraries that are not part of the target's sysroot. (If the library is part of the target's sysroot, then the ldflags: section can be used instead.)

On the target device at runtime, the dynamic linker will look for the library, so it must be made available inside the app sandbox, somewhere in the dynamic linker's library search path. (The dynamic linker will typically look in the /lib and /usr/lib directories for libraries at runtime.)

The library file can be bundled as a part of the app using the bundles: section of the .cdef file.

bundles:
{
file:
{
// Bundle the "foo" library as part of the app (in the app's /lib directory).
libfoo.so.3 /lib/
libfoo.so.3.1.1 /lib/
}
}

Or, if the library is already present on the target, then the files: or dirs: subsection of the requires: section of either the .cdef or .adef file can be used to make the library visible from inside the app sandbox.

requires:
{
file:
{
// Make the "foo" library available inside the app sandbox (in the app's /lib directory).
/usr/local/lib/libfoo.so.3 /lib/
/usr/local/lib/libfoo.so.3.1.1 /lib/
}
}

For backward compatibility, it is also possible to specify the library name "xml" without the leading "lib" or the trailing ".so". This will result in "<c>-lxml</c>" being passed to the linker when linking any executables that include this component, but will not add a '-L' option.

requires:
{
lib:
{
xml // I need access to libxml.so which is expected to already be on the target.
}
}

This is equivalent to using the "ldflags:" section to add "-lxml" to the linker command-line arguments.

ldflags:
{
-lxml
}

Component

Declares this component depends on another component.

requires:
{
component:
{
foo
bar
}
}

Any app that uses a component will also use any other components that component requires, and any components they require, etc.

Specifying a dependency on another component ensures that calls to component initialization functions ( COMPONENT_INIT in C/C++ components ) are sorted in the correct order. If component A depends on component B, then component B will be initialized first.

Dependency loops are not allowed: component C can't depend on another component that (either directly or indirectly) depends on component C. The build tools detect dependency loops and report any error.

Sources

Contains a list of source code files.

If C or C++ code, one source file must implement a COMPONENT_INIT function. The framework will automatically call that function at start-up.

sources:
{
foo.c
bar.c
init.c // This one implements the COMPONENT_INIT
}

Copyright (C) Sierra Wireless Inc. Use of this work is subject to license.