System Definition .sdef

This topic provides details about Legato's System Definition files.

See howToSecurity for details on how to howToSecuritySandboxConfigSample and understand app and process limits.

.sdef files can contain these sections:

Search Paths

These sections are used to specify directories in which mksys should look for the various files that make up your system.

Note
mksys allows the command line arguments -i or --interface-search to also specify the interface search path(s). For backwards compatability, all other search paths fall under the command line arguments -s or --source-search. To add multiple search paths simply add multiple instances of -i or -s to your command.

When building systems that are intended to be inherited by other projects it is recommended to use the search paths within your included .sdef file. This way each individual subsystem can be added to a top level .sdef file without forcing the user to append many search paths to their command line when running mksys.

interfaceSearch

The interface search is used to find the .api files referenced in your system, your applications, and the components that make up your applications.

To allow components to be relocated in the build host file system, components should not specify interface file search paths outside of their own directories. To separate interfaces from implementation, interfaces should be kept outside of components. This can be accomplished by adding interface search directory paths to the interfaceSearch section in the .sdef file.

This way components can be kept in a components directory, and interfaces can be kept within an interface directory. Keeping these API files seperated from compoents can make it easier to share between clients and servers, as well as between seperate server implementations.

So, for example, in a Legato system definition there would be a interface search paths setup for the APIs offered by Legato itself under default.sdef:

interfaceSearch:
{
    $LEGATO_ROOT/interfaces                 // Directory contianing
    $LEGATO_ROOT/interfaces/modemServices   // Directory containing Legato modem services APIs.
}

In your sdef, after defining LEGATO_ROOT you can include Legato's search paths and append your own.

#include "$LEGATO_ROOT/default.sdef"

interfaceSearch:
{
    $CURDIR/interfaces   // Directory containing custom interfaces belonging to this system.
}

Now your components and applications can find all of your custom APIs as well as those that come with Legato.

Note
CURDIR is a mktools managed variable. CURDIR always refers to the directory that your .def file is residing in. So, if your def file is in ~/myProject/mySystem.sdef then CURDIR would be the full path to the def's directory: /home/username/myProject/ See Environment Variables for more details on the automatic variables.

For example, inside of one of your Component.cdef files:

requires:
{
    api:
    {
        le_sms.api       // Make use of one of the modem service APIs.
        myCustomApi.api  // Also, make use of your own custom APIs.
    }
}

appSearch

This search path is used when specifiing apps in your .sdef's apps: section. Instead of having to specify a full path to your applications, you can specify locations for where they should be found.

appSearch:
{
    $CURDIR/applications  // Search for apps in a subdirectory of the directory that holds your
                          // .sdef or .sinc file.
}

componentSearch

Use the componentSearch to add your paths to the directories that the build system uses to find your components.

componentSearch:
{
    $CURDIR/components  // Search for components in a subdirectory of the directory that holds your
                        // .sdef or .sinc file.
}

This way, when you are defining your applications executables within an executables: section you do not need to specify the full path to your component's executables. For example, your application executable is composed of two components, mainComp and helperComp. Using a component search path, you can easily share helperComp between different executables and different applications.

executables:
{
    myExe = ( mainComp helperComp )
}

moduleSearch

The section moduleSearch is for adding to the module search path. This search path is used when specifing modules in your .sdef's kernelModules: section. Instead of having to specify a full path to your kernel modules, you can specify locations for where they should be found.

For example:

moduleSearch:
{
    // My example modules are found in this directory.
    $LEGATO_ROOT/drivers/example
}

// In the kernel modules section, you no longer need to give a full path to your mdef files.  The
// mktools will search through all of your paths given in the moduleSearch section.
kernelModules:
{
    // Include my sample module in the build.
    example.mdef
}

apps

An apps: section declares one or more apps to be deployed to the target system.

apps:
{
webserver
}

This looks for an app definition file called webserver.adef and includes it in the system. You can also optionally include the .adef extension.

Alternatively you can include the full path and adef extension on the declaration and bypass the app search paths.

apps:
{
/full/path/to/my/app/webserver.adef
}

In addition to including applications in source for to your system builds. You can also add binary application packages.

Binary app packages are apps distributed without their source code. You include these apps in your .sdef file apps: section just like .adef files.

apps:
{
webserver.wp85.app
}

Binary app files are named for the target architecture they are built against. This way it's hard to mix up builds for incompatible target architectures. The same pathing and search rules apply to binary apps as do for .adef files.

The apps: section can override limits and other app settings.

Here's a code sample to deploy a web server limiting its share of the CPU under heavy load to 500 (see cpuShare):

apps:
{
webServer
{
cpuShare: 500
}
}

Any of the following subsections can be used in an .sdef apps: section, and will override the .adef setting for all processes in that app:

maxPriority

maxPriority

Sets the maximum priority level the app it permitted to use.

Acts as a ceiling only. Lowers the priority level if an app would otherwise be allowed to use a higher priority. It won't raise the priority level for any processes in the app.

Here's a code sample where a process in the app's .adef is configured to start at high priority, and the .sdef section for that app has maxPriority set to medium so the process will start at medium priority.

apps:
{
foo
{
maxPriority: high
}
}

Another process in the same .adef configured to start at low priority will still start at low priority.

preloaded

Indicates whether or not an app must be preloaded onto the target device separately from the rest of the system.

If you are not sure whether or not you need this feature, you probably don't. Use of this feature is intended for very specific use cases. It is encouraged that delta updates of systems be used instead, whenever practical.

The legato AF supports being installed in a read-only partition, mounted as /mnt/legato in the target file system. Writeable files will be kept in another file system mounted as /legato in the target file system.

If the read-only partition must be updated, but there are other (possibly very large) apps in the writeable file system, it may be impossible to deliver an update containing the apps over-the-air at the same time that the read-only partition is updated.

Usually, the read-only partition does not need to be updated, but in some cases, it may be desireable, and this feature can help.

For example, a customer has a giant app containing pictures and audio files. In the factory, the framework and a few apps are loaded into the read-only /mnt/legato, and other apps, including the huge app, are installed in the writeable /legato. Later, when the device is in the field, a change needs to be made to both the modem firmware and the Legato framework, and must be delivered together, as a single FOTA (firmware over the air) update. A new system is built using mksys. But, the resulting system update file is too large to fit in the FOTA update image (and likely very expensive to deliver over the air to hundreds of thousands of devices). Fortunately, the audio files don't need to be updated at the same time, and the audio app can be marked "preloaded" in the .sdef file to exclude it from the system update file. After the FOTA update, the new system will use the audio file that already exists on the target's writeable file system.

There are 3 different preloaded flag types and each controls the behavior of the MD5 hash compatibility check:

  • buildVersion
  • Explicitly specified MD5 hash
  • anyVersion
Warning
Use preloaded flag at your own risk! If the preloaded app does not exist on the target, or if it is incompatible (on API or binary level) with new Legato, then the system may become unstable or even unusable!

preloaded: buildVersion

Use in the case when the app version installed on the target, matches the most recent source code (in your build environment). That means the MD5 hash generated by the build, will match the MD5 hash of the application installed on the target. "Build version" refers to the MD5 hash of the app, not the version tag specified in the application definition (.adef) file.

apps:
{
modemService
audioService
dataConnectionService
controller
userInterface { preloaded: buildVersion }
}

After you have built the new system, verify that the app that was marked "preloaded" in the .sdef file has the same MD5 hash as the app that is actually installed on the target. You can do this by comparing the contents of the symlink in /legato/systems/current/apps/ on the target with the contents of the symlink in the staging/apps/ directory of the system's build directory on the build host.

$ ssh root@192.168.2.2 'readlink /legato/systems/current/apps/userInterface'
/legato/apps/a60357d912ff3b4b28e080580b34fff3
$ readlink build/wp85/system/staging/apps/userInterface
/legato/apps/a60357d912ff3b4b28e080580b34fff3

If these are different, then something has changed that has resulted in the built version of your app to be different in the context of your new system. If you continue to install your new system on the target, the "preloaded" app will not start.

Note
Option {preloaded: buildVersion} is equivalent to the existing option {preloaded: true}, which is now deprecated.

preloaded: "MD5 hash"

apps:
{
modemService
audioService
dataConnectionService
controller
userInterface { preloaded: a60357d912ff3b4b28e080580b34fff3 }
}

If the MD5 hash of your app (in your build environment) is different from what is installed on the target and you want to use the hash from the target, you can set the preloaded: option to the MD5 hash of your build environment. You must manually check that this version of the app is still compatible with your new system. This will ensure that the app is NOT replaced when the system is loaded on your target. Change the "buildVersion" in your "preloaded: " statement in your .sdef file to the MD5 hash of the old version of the app installed on your target.

preloaded: anyVersion

apps:
{
modemService
audioService
dataConnectionService
controller
userInterface { preloaded: anyVersion }
}

This option allows you to re-use the preloaded app which has MD5 hash different from the app in your build environment, but without explicitly specifying the MD5 hash in the .sdef file. Essentially, you are telling the system to re-use whatever version of the app is currently installed on the target.

This option is convenient if you want to re-use the same update image for multiple targets, and these targets may have different versions (i.e. different MD5 hash) of this app already installed. In this use case, you can specify "anyVersion" option for the preloaded flag. During the system update the installer will find an app with the same name that was installed previously (and was part of the previous system), and establish a proper symlink to it. If the app with the given name is not found, it can not be started.

Note
You have to be absolutely certain that the version of the preloaded app that is installed on any of your targets, is still compatible with the new system you are about to install.

bindings

Lists IPC bindings that connect apps’ external IPC interfaces. They're listed in the extern section of their .adef files. Each binding connects one client-side interface to one server-side interface.

Interfaces use the app name and the interface name, separated by a period (‘.’). The two bound-together interfaces are separated by an arrow ("->").

For example,

apps:
{
vavController
thermostat
airHandlerProxy
}
 
bindings:
{
// Connect the VAV controller to the thermostat
vavController.temp -> thermostat.temp
vavController.setpoint -> thermostat.setpoint
 
// Connect the VAV controller to the supply air duct temperature sensor
vavController.ductTemp -> ductTemperatureSensor.temp
 
// Hook up the VAV control outputs to the damper actuators.
vavController.supplyDamper -> supplyAirDamper.damper
vavController.returnDamper -> returnAirDamper.damper
 
// Use a network proxy to request duct temperature changes from the Air Handling Unit.
vavController.airHandler -> airHandlerProxy.airHandler
}

For security reasons, binding between apps is never performed unless explicitly specified in the .sdef or .adef files.

buildVars

Build environment variables can be defined inside the .sdef file using "buildVars:" sections. This will define variables in the build tools' process environment at build time.

buildVars:
{
    PRODUCT_VERSION = "0.2.3 - beta"
    HARDWARE_REV = 4
}

These are defined using "name = value" pairs, where the value can be a quoted string and may contain the values of other environment variables that were previously defined.

All buildVars: sections will be evaluated before processing any other sections. So, even if a buildVars: section appears after another section that uses it, the variables will be available in that other section.

Note
This is necessary to allow the sharing of components between apps. If two apps contained the same component, but each app were built with a different set of environment variables, it would be hard to tell which set of environment variables were used to build the shared component, and the component may behave in an unexpected way for one of the apps.
apps:
{
    $APP_PATH
}

buildVars:
{
    APP_PATH = path/to/app
}

Within the buildVars: sections, the order of the definitions matters.

buildVars:
{
    X = foo  // X is now "foo"

    X = bar  // X has been changed to "bar"

    X = foo${X}  // X has been changed to "foobar"

    X = "$X baz" // X has been changed to "foobar baz"
}

cflags

Provides a way to specify command-line arguments to pass to the compiler when compiling C source code files. These flags will be added to the flags specified on the command-line and in other definition files.

Flags are separated by whitespace.

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

cxxflags

Provides a way to specify command-line arguments to pass to the compiler when compiling C++ source code files. These flags will be added to the flags specified on the command-line and in other definition files.

Flags are separated by whitespace.

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

ldflags

Linker flags provide 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. These flags will be added to the flags specified on the command-line and in other definition files.

Flags are separated by whitespace.

ldflags:
{
-Lfoo/bar
}

kernelModules

The optional kernelModules: section declares a list of kernel modules to be bundled and installed with Legato.

Each entry represents a path to the .mdef definition file that describes how the module is installed on the target.

This code sample shows the section declaring the "/path/to/kernel/module/hello.mdef" be bundled with Legato:

kernelModules:
{
/path/to/kernel/module/hello
/path/to/kernel/module/world [optional]
}

The kernel modules listed in this section are loaded in alphabetical order and removed in reverse alphabetical order by the Legato system. If a module depends on one or more other modules, then the order in which the modules are installed and removed takes the dependencies into consideration. Dependencies are added by adding the modules to the requires: kernelModules: section of the .mdef for the kernel module that depends on them.

Note
In case a module fails to load, the fault handler kicks in which restarts the Legato framework in order to attempt the recovery of the system. If the framework does not need to be restarted when a module fails to load, the module must be marked as optional by using '[optional]' tag. In the above example, world is an optional module.

commands

To make a command-line tool available to a root user who is logged-in to a shell on a target device tty (e.g., through secure shell [ssh] or a serial console):

  1. Build an app containing the executable.
  2. Add the executable to the "commands:" section of the .sdef file.

Each entry in the commands section looks like this:

commandName = appName:/path/to/exe

The path to the executable must be an absolute path within the application's read-only installed files. For example, if the executable is a script that was bundled into the app "myApp" to appear at "/usr/share/exe" inside the myApp's sandbox at runtime, then the command would be specified as

commandName = myApp:/usr/share/exe

If the executable is built using an "executables" section in a .adef file, then the executable will appear in the app's bin directory. For example,

apps:
{
myTools // This app's .adef builds an exe called "led" that can be used to turn LEDs on and off.
}
 
commands:
{
led = myTools:/bin/led // When I login via ssh, I can run "led 1 on" to turn on LED 1.
}
Warning
When the command runs, it runs with the full privileges of the user that runs it. If you login as root and run a command, the command executes with root user privileges.

externalWatchdogKick

If your system is hooked up to an external watchdog (e.g.; wp85 is externally hooked up to Linux's Softdog) the internal watchdog daemon will "kick" the external watchdog at a specified time as long as all watched processes and apps are running and have successfully "kicked" the internal watchdog.

The externalWatchdogKick sets the interval for the internal watchdog daemon to kick the external watchdog. If no external watchdog exists then externalWatchdogKick will have no effect on the system.

If this section is not specified then the default of 30 seconds will be used.

Example:

externalWatchdogKick: 120000 // Configure external watchdog kick to 2mins

extern

Externs indicate which of your system's RPC API interfaces should be made available for binding to the RPC API interfaces of other systems.

For example,

extern:
{
aliasName = appName.interfaceName
}

This creates an alias for each interface that doesn't include any internal implementation details of your system.

Inside your system, its network-interfaces are identified using the names of the executables and components that implement them. Exposing these details outside your system can create maintainability problems later (see https://en.wikipedia.org/wiki/Information_hiding).

Externally, the network-interface will be identified using the system name and the external alias (systemName.aliasName), thereby hiding from other systems the details of which executable and component implements the interface. Encapsulating the system's implementation details allows them to be changed later without affecting other systems that communicate with this system. Only if a system's external interface changes would any other system be affected.

Use period characters ('.') to seperate components of a network-interface. (E.g., applications and interface names.) External aliases must not contain any period characters ('.').

When the external alias has the same name that the component uses as the interface name, it's possible to save time by omitting the aliasName = part. For example,

extern:
{
appName.interfaceName
}

This is equivalent to

extern:
{
interfaceName = appName.interfaceName
}

To use this extern feature, RPC must be enabled when building the Legato framework.

links

The links: section declares a list of network communication links to be bundled and installed with Legato, as a part of supporting Legato APIs API Files across multiple remote-host systems that are networked together (i.e. inter-system (RPC) communication).

Each entry identifies a component definition and an optional list of command-line arguments, used to declare run-time parameters for creating/opening a communication channel to a specific remote-host system, such as IP address, listening port number, baud rate, etc..

Format:

linkName = (<componentName>, "arg1", "arg2", ...)

The component definition file provides the le_comm.h implementation of the RPC communication channel for a system, and is required in order for remote system communication to be established during system run-time. The Legato RPC communication API, le_comm.h, provides a standard interface for facilitating all system-to-system network-layer communication.

For example,

links:
{
LINK1 = (networkSocket "10.0.0.5" "54323")
}

This looks for a component definition file Component.cdef (.cdef Files) in a components directory called networkSocket and includes it in the system.

Alternatively you can include the full path on the declaration and bypass the system componentDirs search paths.

links:
{
LINK1 = (/full/path/to/my/network/networkSocket "10.0.0.5" "54323")
}

To use the links feature, RPC must be enabled when building the Legato framework.