All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Application Definition .adef

.adef can contain these sections:

Bindings

This section allows client-side IPC API interfaces (listed in the “requires" sections of Component.cdef files) to be "bound" to server-side interfaces (listed in the "provides" sections of Component.cdef files).

This allows direct control over how IPC interfaces are interconnected, thereby allowing reusable components and/or apps to be "wired" together to form a working system.

Bindings can be between two interfaces inside the same app (an "internal binding"), or can be between a client-side interface in this app and a server-side interface "provided" by another app (or non-app user).

Interface instances inside the app are identified using the executable name, component name, and the service instance name. These different parts of the interface instance identifier are separated by a period (‘.’).

The client interface always appears first, and an arrow ( -> ) is used to separate the client interface from the server interface.

For example, to bind "clientExe.clientComponent.clientInterface" to "serverExe.serverComponent.serverInterface":

bindings:
{
    clientExe.clientComponent.clientInterface -> serverExe.serverComponent.serverInterface
}

External services provided by other apps are identified using the app name and the service name, separated by a period ('.').

bindings:
{
    clientExe.clientComponent.clientInterface -> otherApp.serverInterface
}

To bind to a service provided by a non-app user, the app name is replaced by a user name inside angle brackets.

bindings:
{
    clientExe.clientComponent.clientInterface -> <root>.serverInterface
}

In the unlikely event that you built your client-side code using the ifgen tool or mkcomp (Make Component) or mkexe (Make Executable) and then you used "requires" or "bundles" sections to get that client into your application, then the tool reading your .adef file won't know about the client-side interface. In such cases, you'll have to use a special work-around to bind those interfaces:

bindings:
{
    *.le_data -> dataConnectionService.le_data
}

This would bind any unknown client-side "le_data" interfaces in the current app to the "le_data" server-side interface on the "dataConnectionService" app.

Bundles

Lists additional files or directories to be copied from the build host into the application 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 application.
        // 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 on them, 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.

Warning
Access permissions aren't implemented yet. For now, everything that gets installed in the application's "bin" directory on target gets "rx" permission, and all other files get "r" while all directories get "rx".
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.

This is an absolute path inside the application's sandbox file system.

If this ends in a '/', then it is a directory path into which the source object (file or directory) will be copied, and the destination object will have the same name as the source object.

If it does not end in a '/', it is a full destination object path and the destination object may have a different name than the source object.

Note
If the application is running unsandboxed, the bundled files and directories can be found in their installation location under "/opt/legato/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 application 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 application.

CPU Share

Specifies the relative cpu share for an application.

Cpu share is used to calculate the cpu percentage for a process relative to all other processes in the system. New cgroups and processes default value of 1024 if not otherwise configured. The actual percentage of the cpu allocated to a process is calculated like this:

(share value of process) / (sum of shares from all processes contending for the cpu)

All processes within a cgroup share the available cpu percentage share for that cgroup like this:

  • cgroupA is configured with the default share value, 1024.
  • cgroupB is configured with 512 as its share value.
  • cgroupC is configured with 2048 as its share value.
  • cgroupA has one process running.
  • cgroupB has two processes running.
  • cgroupC has one process running.

This assumes all processes in cgroupA, cgroupB and cgroupC are running and not blocked waiting for an I/O or timer event, and another system process is also running.

Sum of all shares (including the one system process) is 1024 + 512 + 2048 + 1024 = 4608

The process in cgroupA will get 1024/4608 = 22% of the cpu. The two processes in cgroupB will share 512/4608 = 11% of the cpu, each process getting 5.5%. The process in cgroupC will get 2048/4608 = 44% of the cpu. The system process will get 1024/4608 = 22% of the cpu.

cpuShare: 512

Executables

Lists executables to be constructed and moved to the “bin” directory inside the app.

An executable’s content is specified inside parentheses after the name, and can be one of these:

  • relative file system path to source code file (e.g., a .c, .java, or .lua file)
  • component
  • relative file system path to library file (e.g., .a or .so file).
Warning
Only C/C++ source files, components, and libraries are currently supported.
executables:
{
    myexe1 = ( file1.c libgoodstuff.so )
    myexe2 = ( foobar toto )
}

If a component is included in an executable, then that component will be built and linked into the executable, and its COMPONENT_INIT function will be run at process start-up. Furthermore, that component’s runtime files (shared libraries, Lua source code, etc.) will be packaged inside the application for installation on the target.

If a source file is included in an executable, the build tools will search for it in the file system according to the source search path. When found, it will be compiled (if written in a compiled language) and linked into the executable. Note that all the source files specified for an executable will together be considered a component (which will have the same name as the executable), and one of those source files will be expected to implement a COMPONENT_INIT function, which will get called at start-up.

If a library (.so or .a file) is “included in” an executable in this way, that library is expected to be found in the “requires” or “bundles” section of the application’s .adef file or the Component.cdef file of a component that is included in the application. When the executable is linked, it will be linked with these libraries in the order in which they are listed. Note that it is permitted to list the same library more than once, as is sometimes necessary in order to satisfy all unresolved symbols, due to the single-pass nature of the linker.

The mechanism by which the executable is constructed will depend on the type of content and the target device. For example, C and C++ files will be compiled and linked using the appropriate compiler tool chain, depending on what target device the application is being built for. The same goes for components written in C and C++. Java code will be compiled to Java bytecode. Interpreted code, such as Python code will be simply copied into the application.

Note
In some cases, there may be incompatibilities between components or source code files that prevent them from being included in the same executable. For example, you may not be able to put Java sources and C sources into the same executable together. If you do violate one of these restrictions, the build tools will tell you about it.

Groups

Add an app's user to groups on the target system.

groups:
{
    www-data
    modem
}

Max File System Bytes

Specifies the maximum amount of RAM that can be consumed by an app's temporary (volatile) file system at runtime.

Default is 128K

maxFileSystemBytes: 120K
Note
The file system size will also be limited by the Max Memory Bytes limit.

Max Memory Bytes

Specifies the maximum amount of memory (in bytes) that all processes in an app can share. Default is 40960K (40MB).

maxMemoryBytes: 1000K
Note
Will be rounded to the nearest memory page boundary.

Max MQueue Bytes

Specifies the maximum number of bytes that can be allocated for POSIX MQueues. Default is 512.

maxMQueueBytes: 16K

Max Queued Signals

Specifies the maximum number of signals that can be waiting for delivery to processes in the app.

This limit will only be enforced when using sigqueue() to send a signal. Signals sent using kill() are limited to at most one of each type of signal anyway.

Default is 100.

maxQueuedSignals: 200

Max Threads

Specifies the maximum number of threads allowed to run at one time: an integer number.

Note
A single-threaded process (a running program that doesn't start any threads other than the one running the main() function) counts as one thread.

If fork() calls or pthread_create() calls start failing with error code EAGAIN (seen in strace output as clone() system calls), then you are probably running into this limit.

Default is 20.

maxThreads: 4

Processes

A processes section specifies processes to run when the app is started, along with their environment variables, command-line arguments, limits, and fault handling actions.

Content in the processes section is divided into subsections.

If different processes have different variables, they must be in separate processes: sections.

processes:
{
    // Start up these processes when the app starts
    run:
    {
        myProc1 = ( myExe --foo -b 43 )
        myProc2 = ( myExe –bar --b 92 )
        ( myExe2 "Hello, world." )  // If no proc name is specified, uses the exe name by default.
    }

    // Env var settings (name = value) for all processes in this section.
    envVars:
    {
        LE_LOG_LEVEL = DEBUG
    }

    priority: medium    // Starting (and maximum) scheduling priority.
                        // Process can only lower its priority from here.

    maxCoreDumpFileBytes: 100K  // Maximum size of core dump files.
    maxFileBytes: 50K           // Files are not allowed to grow bigger than this.
    maxLockedMemoryBytes: 32K   // Can't mlock() more than this many bytes.
    maxFileDescriptors: 20      // Can't have more than this number of FDs open at the same time.
}

processes:
{
    run:
    {
        ( realTimeExe )
    }

    priority: rt10   // Allow real-time scheduling (max priority 10) for processes in this section.

    /*-- Exception handling policy for processes in this section. --*/
    faultAction: restart   // Restart the process if it fails.
}

The .adef processes section has these subsections.

run

Names a process to be started by the Supervisor when the app is started. Also specifies executable and command-line arguments.

run:
{
myProc1 = ( myExe --foo -b 43 )
}

Process name and command-line arguments are optional.

run:
{
( myexe )
}

If the process name is not specified, then the process will be given the same name as the executable it is running (e.g. myexe)

A given executable can be launched multiple times.

run:
{
process1 = ( myexe )
process2 = ( myexe )
}

Command-line arguments passed to the process when started can appear after the executable name.

run:
{
( myexe --foo )
}
run:
{
( myexe --bar // Note that the command-line can be broken into multiple lines for readability.
--toto ) // And it can be commented too.
}

Executable names can be the ones listed in the app’s “executables:” section. They can also be the names of files that are "bundled" into the app with [x] (executable) permission.

Quotation marks (either single ' or double ") can be used if white-space (spaces, tabs, //, etc.) is needed inside a command-line argument, or if an empty argument is needed ("").

Env Vars

Environment variables appear as "name = value" pairs. The first value is the environment variable name; the second part is the variable value. Enclose the value in quotation marks (either single ' or double ") if white-space is required.

envVars:
{
LE_LOG_LEVEL = DEBUG
MESSAGE = "The quick brown fox jumped over the lazy dog."
}

Fault Action

This subsection specifies the action that the Supervisor should take when the process terminates with a non-zero exit code or because of an un-caught signal (e.g., SIGSEGV, SIGBUS, SIGKILL).

Possible values are:

  • ignore - the Supervisor just logs a warning message but will take no further action.
  • restart - log a critical message and then restart the process.
  • restartApp - log a critical message and then restart the entire application.
  • stopApp - log a critical message then terminate the entire application (send all processes the SIGTERM signal, followed shortly by SIGKILL).
  • reboot - log an emergency message and reboot the system.

Default is ignore.

faultAction: restart

Priority

Specifies the starting (and maximum) scheduling priority. A running app process can only lower its priority from this point. Once it has lowered its priority, it can't raise it again (e.g., if the process starts at medium priority and reduces to low priority, it can't go back to medium priority). The default is medium.

Values:

  • idle - for very low priority processes that only get CPU time if no other processes are waiting for the CPU.
  • low, medium, high - intended for normal processes that contend for the CPU. Processes with these priorities don't preempt each other, but their priorities affect how they're inserted into the scheduling queue (high to low).
  • rt1 to rt32 - intended for (soft) realtime processes. A higher realtime priority will preempt a lower realtime priority (ie. "rt2" would preempt "rt1"). Processes with any realtime priority will preempt processes with high, medium, low and idle priorities.
Warning
Processes with realtime priorities preempt the Legato framework processes. Ensure that your design lets realtime processes relinquish the CPU appropriately.
priority: medium

Max Core Dump File Bytes

Specifies the maximum size (in bytes) of core dump files that can be generated by processes.

Default is 8K

Note
The core dump file size is limited by Max File Bytes. And if the the core file will be generated in the sandbox's temporary runtime file system, it will also be limited by Max File System Bytes.
maxCoreDumpFileBytes: 100K
maxFileBytes: 100K
maxFileSystemBytes: 200K

Max File Bytes

Specifies the maximum size processes can make files. The K suffix permits specifying in kilobytes (multiples of 1024 bytes).

Default is 88K

maxFileBytes: 200K

Exceeding this limit will result in a SIGXFSZ signal being sent to the process. By default, this will kill the process, but it can be blocked or caught, in which case the code that tried to expand the file will receive an error with errno set to EFBIG.

Note
If the file is in the sandbox's temporary runtime file system, the file size will also be limited by Max File System Bytes and Max Memory Bytes.

Max File Descriptors

Specifies the maximum number of file descriptors a process can have open at one time.

Default is 256

maxFileDescriptors: 100

Max Locked Memory Bytes

Specifies the maximum bytes of memory the process can lock into physical RAM (e.g., using mlock() ).

Can't be higher than Max Memory Bytes.

Default is 8K

maxLockedMemoryBytes: 100K
Note
This also limits the maximum number of bytes of shared memory that the app can lock into memory using shmctl().

Watchdog Action

This subsection specifies what action the Supervisor should take when a process that has subscribed to the watchdog service fails to kick the watchdog before it expires. The possible values are the same as in Fault Action with the addition of:

stop - the Supervisor terminates the process if it is still running. If no watchdog action has been supplied then the default action is to restart the process.

Watchdog Timeout

This subection specifies the length of the timeout (in milliseconds) for watchdogs called by processes in the enclosing processes section. Once a process has called le_wdog_Kick() it is registered with the software watchdog service and if it then fails to call le_wdog_Kick() within the given timeoutout, the Supervisor will be notified and the Supervisor will take the action specified in Watchdog Action.

Provides

The "provides:" section is used to specify things that the application provides to other apps (or non-app processes) in its runtime environment.

For now, only "api:" subsections are supported.

API

This subsection declares that a server-side IPC API interface is available for other apps (or non-app users) to use via Bindings.

For example, the following declares that the "temperature" interface on the "thermistor" component in the "sensor" executable should be made into a bindable external interface called "spaceTemp":

provides:
{
    api:
    {
        spaceTemp = sensor.thermistor.temperature
    }
}

If the application were called "thermometer", then other applications could bind their client-side interfaces to "thermometer.spaceTemp" to receive temperature readings from this sensor app.

Internal interface instances are identified using the executable name, component name, and component's interface name. These different parts of the interface instance identifier are separated by a period (‘.’).

Requires

The "requires:" section is used to specify things that the application needs from its runtime environment.

It can contain various subsections:

API

Declares that a client-side IPC API interface on an executable inside the app needs to be bound to a service provided by something outside the app.

Interfaces inside the app are identified using the executable name, the component name, and the component's interface name, with each of these parts separated from the others by a period (‘.’).

For example, the following declares that the "temperature" interface on the "tempInput" component in the "controller" executable should be made into a bindable external interface:

requires:
{
    api:
    {
        controller.tempInput.temperature
    }
}

Outside the application, this client-side interface will be called "temperature" (thereby hiding from other apps the details of what executable and component implements the interface).

If the application developer wants other apps to see this interface as having a different name, it can be given a different name by adding "alias = " in front of the interface specification. For example,

requires:
{
    api:
    {
        airTemp = controller.tempInput.temperature
    }
}

If this app were called "thermostat", then in a .sdef file, the interface would be called "thermostat.airTemp".

Config Tree

Declares that the application requires access to a given configuration tree. Each application has its own configuration tree named after the application. In addition, there's a "system" configuration tree that contains privileged system data.

By default, an application only has read access to its own configuration tree.

Applications can be granted read access or both read and write access to trees using an optional access permission specifier:

  • [r] - grant read-only access
  • [w] - grant read and write access

If access is granted to a tree, but the access mode ("[r]" or "[w]") is not specified, then only read permission will be granted.

A special tree name "." can be used to refer to the app's own configuration data tree.

requires:
{
    configTree:
    {
        [w] .       // I need write access to my configuration data.
        otherApp    // I need read access to another app's configuration data.
    }
}
Warning
Because configuration data may be saved to flash storage, granting write access to a configuration tree can make it possible for the application to wear out your flash device. Also, remember that granting an application write access to another application's configuration settings creates a security hole, because it makes it possible for one application to interfere with the operation of another application. It is especially dangerous to grant write access to the "system" tree. Doing so may make it possible to completely compromise the target device, either accidentally or deliberately if a hacker breaks into the app. Even read access may be dangerous if any kind of security key, including PIN and PUK codes, are kept in the system tree. As a general rule, apps should never be given direct access to the "system" tree.

Dir

Used to declare that certain directories that reside on the target device outside of the application are to be made accessible to the application.

The location inside the application'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 application. 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 is 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
Just because the directory is made to appear in the application's sandbox doesn't mean that the application will be able to access it. The permissions settings on the directory 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 made accessible from inside an application 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 is not possible to put anything inside of a directory that was mapped into the application from outside of the sandbox. For example, if you "require" /bin to appear at /usr/bin, you cannot then "bundle" a file into /usr/bin or "require" something to appear in /usr/bin, since that would have an effect on the contents of the /bin directory outside of the application.

File

Used to declare that certain files that reside on the target device outside of the application are to be made accessible to the application.

The location inside the application's sandbox at which the file 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 object in the file system outside of the application. This must be an absolute path (beginning with ‘/’).
  • The second path is the 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 never end in a '/'.

If the second path ends in a '/', then it is 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:
{
    file:
    {
        // I get character stream input from outside via a named pipe (read-only)
        /var/run/someNamedPipe  /var/run/

        // I need read and write access to UART2.
        /dev/ttyS1  /dev/

        // 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
Just because the file system object is made to appear in the application's sandbox doesn't mean that the application will be able to access it. The permissions settings on the file must also allow it. 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 is also possible to give the object a different name inside the sandbox than it has outside the sandbox. This is done by adding a name to the end of the second path.

requires:
{
    file:
    {
        /dev/ttyS0  /dev/port1     // Program uses /dev/port1, but UART0 is called /dev/ttyS0.
    }
}
Warning
Any time anything is made accessible from inside an application 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?

Sandboxed

Specifies if the app will be launched inside a sandbox.

Permitted content in this section is:

  • true - app will be run inside of a sandbox.
  • false - app won't be run in a sandbox.

The default is true.

If an app is not inside of a sandbox (unsandboxed), it can see the target device's real root file system. A sandboxed app can't see the target's real root file system; instead, a sandboxed app has its own, separate root file system (which it can't leave).

Each app has its own user ID and primary group ID. The app's user name and primary group name are both "appxxxx", where the "xxxx" is replaced with the name of the app.

While a user and/or group will be automatically created if missing for the specified app, only users and groups of sandboxed apps will be automatically deleted when those apps are uninstalled.

sandboxed: false

Start

Specifies if the app should start automatically at start-up: "auto" sets the app to start automatically by the Supervisor. "manual" sets the app to start through a manual prompt to the Supervisor. Default is auto.

start: auto

Version

Optional field that specifies a string to use as the application's version string.

version: 0.3a
Note
  • The –append-to-version option to mkapp can be used to add to the application's version string.
  • app foo version can be run on-target to get the version string of the app called "foo".

Watchdog Action

The watchdogAction section sets the recovery action to take if a process in this application expires its watchdog. This value will be used if there is no value set in the processes section for a given process. See Watchdog Action in the processes section.

Watchdog Timeout

The watchdogTimeout section sets the default timeout in milliseconds for processes in this application. This value will be used if there is no value set in the processes section for a given process. See Watchdog Timeout in the processes section.


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