App Control API

API Reference

API for controlling (starting, stopping, and debugging) apps.

Warning
This API should only be made available to privileged users because it allows denial of service and arbitrary program execution in other apps. It also gives the user the ability to grant themselves access to any file, socket or device on the target system.

Binding

You can use a definition .adef file to bind your client-side app and component to the server-side App Control service.

This code sample shows how to bind an app to this service:

bindings
{
myExe.myComp.le_appCtrl -> <root>.le_appCtrl
}

See Application Definition .adef for details.

Start App

Use le_appCtrl_Start() (const char * appName) to start an app.

The code sample shows how to use the App Control service to start an app:

le_result_t result = le_appCtrl_Start("myApp");

where myApp is the name of the app.

Stop App

Use le_appCtrl_Stop() to stop an app.

This code sample shows how to use the App Control service to stop an app.

le_result_t result = le_appCtrl_Stop("myApp");

where myApp is the name of the app.

Debugging Features

Several functions are provided to support the construction of tools for debugging apps. These functions are used by the appCtrl and sbtrace tools. See the implementations of those tools (under framework/tools/target/linux) for real-world examples of how these functions can be used.

  • le_appCtrl_GetRef() can be used to get a reference to an app. This reference can then be used to apply overrides to the way that app will be started when le_appCtrl_Start() is called.
  • le_appCtrl_SetRun() can be used to set or clear the "run" flag for a given process in the app. Clearing the "run" flag will result in that process not being started when the app starts. This can be used to start an app without one or more processes that are to be started later using strace or gdbserver, for example.
  • le_appCtrl_SetDebug() can be used to set or clear the "debug" flag for a given process in the app. Setting the "debug" flag will result in the process being started in the stopped state. A debugger can then be attached to the process.
  • le_appCtrl_Import() can be used to import (into an app's container) something from the file system outside of the app's container. This is used by the sbhelper and sbtrace interactive sandbox configuration helper tools.
  • le_appCtrl_SetDevicePerm() is used to set the permissions of a device file. This is also used by the sbhelper and sbtrace interactive sandbox configuration helper tools.
  • le_appCtrl_AddTraceAttachHandler() is used to register a call-back handler function to be called whenever the Supervisor has created a process in the app. After calling this call-back, the Supervisor will wait until le_appCtrl_TraceUnblock() is called for that process before allowing it to run. This gives a debugger the opportunity to attach to the process before running it.
  • le_appCtrl_ReleaseRef() releases the reference returned by le_appCtrl_GetRef() and resets all the app control overrides that were set using that reference.

Synchronizing Process Start

Some tools need to "attach" to a process (e.g., using the Unix ptrace() API) when the process starts. If the process is started by the Supervisor when the app starts, then synchoronization can be achieved by calling le_appCtrl_AddTraceAttachHandler() to register a "Trace Attach Handler" call-back for the app before starting it. When the Supervisor starts an app that has a Trace Attach Handler registered for it, the Supervisor will prepare the app container, as usual, and for each app process it starts, it will fork() a child process, put it into its runtime container, call the Trace Attach Handler function and block the child process before exec()ing the app's program. When the Trace Attach Handler function is called, it is passed the PID of the child process, which the debug tool can use to attach to the process before calling le_appCtrl_TraceUnblock() to ask the Supervisor to unblock the app's process.

{
...
 
// Get a reference to the app.
le_appCtrl_AppRef_t appRef = le_appCtrl_GetRef(appNameStr);
if (appRef == NULL)
{
fprintf(stderr, "App '%s' could not be started. Check logs for more info.\n", appNameStr);
exit(EXIT_FAILURE);
}
 
// Set an attach handler.
le_appCtrl_AddTraceAttachHandler(appRef, AttachHandler, NULL);
 
// Start the app.
le_result_t result = le_appCtrl_Start(appNameStr);
if (result != LE_OK)
{
fprintf(stderr, "App '%s' could not be started. Check logs for more info.\n", appNameStr);
exit(EXIT_FAILURE);
}
}
 
static void AttachHandler
(
le_appCtrl_AppRef_t appRef, ///< [IN] App reference.
int32_t pid, ///< [IN] PID of the process to attach to.
const char* procNamePtr, ///< [IN] Name of the process.
void* contextPtr ///< [IN] Not used.
)
{
// Attach to the process.
...
 
// Ask the supervisor to unblock the process.
}

Supressing Start of a Process

It's possible to ask the Supervisor to start an app without starting one or more of the app's processes. This is done by calling le_appCtrl_SetRun() to set the "Run" flag to false for the process before calling le_appCtrl_Start() to start the app.

{
...
 
// Get a reference to the app.
le_appCtrl_AppRef_t appRef = le_appCtrl_GetRef(appNameStr);
if (appRef == NULL)
{
fprintf(stderr, "App '%s' could not be started. Check logs for more info.\n", appNameStr);
exit(EXIT_FAILURE);
}
 
le_appCtrl_SetRun(appRef, procNameStr, false);
 
le_appCtrl_Start(appNameStr);
}

Importing Files Into App Containers

le_appCtrl_Import() can be used to import things from the real root file system into an app's run-time container. le_appCtrl_SetDevicePerm() can be used to set the access permissions for a device file such that the app can access it.

le_result_t r = le_appCtrl_Import(appRef, path);
 
if (r != LE_OK)
{
fprintf(stderr, "Could not import file '%s'. %s.\n", path, LE_RESULT_TXT(r));
}
else if (IsDevice(path))
{
// Give the app read access to the device file.
if (le_appCtrl_SetDevicePerm(appRef, path, "r") != LE_OK)
{
fprintf(stderr, "Could not set permissions to %s for '%s'.\n", permStr, path);
}
}