Integration#

Configuration#

The Launch Manager is currently configured with a single json file. See Configuration and Configuration examples for details about the Launch Manager configuration format.

Generating flatbuffer file#

The json configuration has to be converted to a flatbuffer file before it can be used by the Launch Manager.

The provided bazel function launch_manager_config handles the compilation of the json configuration to flatbuffer files.

load("@score_lifecycle//:defs.bzl", "launch_manager_config")

# This is your launch manager json configuration
exports_files(["lm_config.json"])

# Afterwards, you can refer to the generated flatbuffer files with :example_config_gen
# The generated flatbuffer file has identical as the json configuration file, but with ".bin" file ending (example: "lm_config.bin")
launch_manager_config(
    name ="example_config_gen",
    config="//scripts/config_mapping:lm_config.json"
)

Location of the configuration file#

The Launch Manager can be started with a CLI flag defining the absolute or relative path to the configuration file:

launch_manager -c <config path>

Example: launch_manager -c /opt/launch_manager/etc/lm_config.bin

If -c is not specified, the Launch Manager will attempt to load the configuration from the default path etc/launch_manager_config.bin.

State manager mw::com config#

An example mw::com config is provided with the following target //score/launch_manager:example_com_config. This is a single config that includes both Launch Manager and the State Manager instance definition so they can be used by both.

For more information about the mw::com configuraiton please refer to their documentation and tutorial at documentaiton https://eclipse-score.github.io/communication/v0.5.0/score/mw/com/doc/tutorial/chapter_2/README.html#separate-configuration-files-for-provider-and-consumer

LmControlService service type#

LmControlService service type
{
  "serviceTypes": [
    {
      "serviceTypeName": "/score/mw/lifecycle/LmControlService",
      "version": {
        "major": 1,
        "minor": 0
      },
      "bindings": [
        {
          "binding": "SHM",
          "serviceId": 7101,
          "events": [
            {
              "eventName": "ActivationResult",
              "eventId": 1
            }
          ],
          "methods": [
            {
              "methodName": "ActivateRunTarget",
              "methodId": 2
            },
            {
              "methodName": "GetActiveRunTarget",
              "methodId": 3
            }
          ]
        }
      ]
    }
  ],
  "serviceInstances": [
    {
      "instanceSpecifier": "LaunchManager/StateManager/Instance",
      "serviceTypeName": "/score/mw/lifecycle/LmControlService",
      "version": {
        "major": 1,
        "minor": 0
      },
      "instances": [
        {
          "instanceId": 1,
          "asil-level": "QM",
          "binding": "SHM",
          "events": [
            {
              "eventName": "ActivationResult",
              "numberOfSampleSlots": 8,
              "maxSubscribers": 1
            }
          ],
          "methods": [
            {
              "methodName": "ActivateRunTarget",
              "queueSize": 1
            },
            {
              "methodName": "GetActiveRunTarget",
              "queueSize": 1
            }
          ]
        }
      ]
    },
    {
      "instanceSpecifier": "StateManager/LaunchManager/Instance",
      "serviceTypeName": "/score/mw/lifecycle/LmControlService",
      "version": {
        "major": 1,
        "minor": 0
      },
      "instances": [
        {
          "instanceId": 1,
          "asil-level": "QM",
          "binding": "SHM",
          "events": [
            {
              "eventName": "ActivationResult"
            }
          ],
          "methods": [
            {
              "methodName": "ActivateRunTarget",
              "queueSize": 1
            },
            {
              "methodName": "GetActiveRunTarget",
              "queueSize": 1
            }
          ]
        }
      ]
    }
  ],
  "global": {
    "asil-level": "QM"
  }
}
Service types#
  • serviceTypeName

    .serviceTypes[].serviceTypeName: "/score/mw/lifecycle/LmControlService" - This is the name of the Launch Manager service.

  • version

    .serviceTypes[].version: { "major": 1, "minor": 0 } - This is the version of the service interface.

  • binding

    .serviceTypes[].bindings[].binding: "SHM" - This is the communication mechanism.

    .serviceTypes[].bindings[].serviceId: 7101 - This is the numeric identifier of the service for this binding. Note this can be changed to any number, however has to be unique to any other service used by this application.

  • events

    .serviceTypes[].bindings[].events[] - Events offered by the service. Each event has a name and a numeric id. Note the ID can be changed to any number, however has to be unique to any other binding used by this application.

    {
      "eventName": "ActivationResult",
      "eventId": 1
    }
    
  • methods

    .serviceTypes[].bindings[].methods[] - Methods offered by the service. Each method has a name and a numeric id. Note the ID can be changed to any number, however has to be unique to any other binding used by this application.

    {
      "methodName": "ActivateRunTarget",
      "methodId": 2
    },
    {
      "methodName": "GetActiveRunTarget",
      "methodId": 3
    }
    
Service instances#

The serviceInstances array defines the concrete instances of a service type. Wwo instances of LmControlService are required, one used by the Launch Manager (LaunchManager/StateManager/Instance) and one used by the State Manager (StateManager/LaunchManager/Instance).

  • instanceSpecifier

    .serviceInstances[].instanceSpecifier: "LaunchManager/StateManager/Instance" - This is the name an application uses to look up the instance.

  • serviceTypeName

    .serviceInstances[].serviceTypeName: "/score/mw/lifecycle/LmControlService" - This is the service type the instance is created from. It must match a serviceTypeName in serviceTypes.

  • version

    .serviceInstances[].version: { "major": 1, "minor": 0 } - This is the version of the service type and must match the one in serviceTypes.

  • instances

    .serviceInstances[].instances[].instanceId - Numeric identifier of the instance. Note the ID can be changed to any number, however has to be unique to any other instance used by this application.

    .serviceInstances[].instances[].asil-level - Safety integrity level of the instance, Either QM or ASIL-B.

    .serviceInstances[].instances[].binding - Communication mechanism, shall be SHM.

    .serviceInstances[].instances[].events[] - Per-method settings, for each method methodName.

    .serviceInstances[].instances[].numberOfSampleSlots - Number of sample slots allocated for the event. This is only configured on the server instance i.e. LaunchManager/StateManager/Instance.

    .serviceInstances[].instances[].maxSubscribers - Maximum number of simultaneous subscribers. This is only configured on the server instance i.e. LaunchManager/StateManager/Instance.

    .serviceInstances[].instances[].methods[] - Per-method settings, for each method methodName.

    .serviceInstances[].instances[].queueSize - Number of calls that can be queued for the method.

    {
      "instanceId": 1,
      "asil-level": "QM",
      "binding": "SHM",
      "events": [
        {
          "eventName": "ActivationResult",
          "numberOfSampleSlots": 8,
          "maxSubscribers": 1
        }
      ],
      "methods": [
        {
          "methodName": "ActivateRunTarget",
          "queueSize": 1
        }
      ]
    }
    
  • global

    .global.asil-level: "QM"

    This is the default safety integrity level for the whole configuration.

Implementation#

Starting a process#

A reporting process must signal to the Launch Manager that it has started successfully by calling the lifecycle API for the language it is implemented in.

A ready_timeout_ms can be configured for each component. If the process does not report Running within that timeout, the Launch Manager terminates the process and registers an error.

Stopping a process#

A process can be configured to terminate in 2 ways:

  • Self-terminating - The process terminates whenever it wants.

  • Non-self-terminating - The process remains alive until LCM requests the process to end using a SIGTERM signal.

For a process to support being terminated by LCM, it needs to respond to a SIGTERM signal by ending ongoing tasks, freeing resources and exiting. Note that a Self-terminating also recieves the SIGTERM signal when LCM decides to deactivate it.

If a process does not terminate after receiving a SIGTERM, and a shutdown_timeout_ms is configured, then after the timeout is fired a SIGKILL is sent and the process is forcefully terminated.

Examples#

C Reporting Process
/********************************************************************************
 * Copyright (c) 2026 Contributors to the Eclipse Foundation
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Apache License Version 2.0 which is available at
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * SPDX-License-Identifier: Apache-2.0
 ********************************************************************************/

#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>

#include "score/mw/lifecycle/report_running.h"

const struct timespec interval = {
    .tv_sec = 0,
    .tv_nsec = 50000000,
};

volatile sig_atomic_t exitRequested = 0;

static void sigTermHandler(int signal)
{
    exitRequested = 1;
}

int main(int argc, char* argv[]){

    assert(signal(SIGTERM, sigTermHandler));

    // report running
    int8_t result = score_mw_lifecycle_report_running();

    // check report was successful
    if(result != 0){
        printf("Failed to report kRunning to LCM! Terminating");
        return 1;
    }

    // wait until lm wants to terminate
    while(exitRequested == 0){
        nanosleep(&interval, NULL);
    }

    return 0;
}
C++ Reporting Process
/********************************************************************************
 * Copyright (c) 2026 Contributors to the Eclipse Foundation
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Apache License Version 2.0 which is available at
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * SPDX-License-Identifier: Apache-2.0
 ********************************************************************************/

#include <unistd.h>
#include <atomic>
#include <cassert>
#include <csignal>
#include <cstdlib>
#include <iostream>

#include <score/mw/lifecycle/report_running.h>

volatile std::sig_atomic_t exitRequested{0};

void sigTermHandler(int)
{
    exitRequested = 1;
}

int main(int argc, char* argv[])
{

    // register signal handler so that lcm can request a termination
    assert(signal(SIGTERM, sigTermHandler));

    // report running state
    score::mw::lifecycle::report_running();

    // do some work

    // wait until lm wants to terminate
    while (exitRequested == 0)
    {
        pause();
    }

    return EXIT_SUCCESS;
}
Rust Reporting Process
// *******************************************************************************
// Copyright (c) 2026 Contributors to the Eclipse Foundation
//
// See the NOTICE file(s) distributed with this work for additional
// information regarding copyright ownership.
//
// This program and the accompanying materials are made available under the
// terms of the Apache License Version 2.0 which is available at
// <https://www.apache.org/licenses/LICENSE-2.0>
//
// SPDX-License-Identifier: Apache-2.0
// *******************************************************************************

use std::sync::atomic::{AtomicBool, Ordering};

const SIGTERM: i32 = 15;

static STOP: AtomicBool = AtomicBool::new(false);

extern "C" {
    fn signal(signum: i32, handler: extern "C" fn(i32)) -> usize;
}

extern "C" fn sigterm_handler(_: i32) {
    STOP.store(true, Ordering::Relaxed);
}

fn main() -> Result<(), &'static str> {
    // register signal handler so that lcm can request a termination
    unsafe {
        signal(SIGTERM, sigterm_handler);
    }

    // report running and check result
    if !lifecycle_client_rs::report_running() {
        return Err("Failed to report execution state");
    }

    // do work until lm wants to terminate
    while !STOP.load(Ordering::Relaxed) {
        std::thread::sleep(std::time::Duration::from_millis(100));
    }

    Ok(())
}

The Lifecycle Interface also offers the run_application API, which removes the boilerplate from a reporting process.

It: - Handles the SIGTERM signal. - Structures the application into an initialization phase and a running phase. - Reports the Running state in the background once initialization is done.

C++ Lifecycle Application
/********************************************************************************
 * Copyright (c) 2026 Contributors to the Eclipse Foundation
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Apache License Version 2.0 which is available at
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * SPDX-License-Identifier: Apache-2.0
 ********************************************************************************/

#include <cstdlib>

#include <score/mw/lifecycle/application.h>
#include <score/mw/lifecycle/runapplication.h>

class LifecycleApp final : public score::mw::lifecycle::Application
{
  public:
    // called before the process reports Running to the Launch Manager
    std::int32_t Initialize(const score::mw::lifecycle::ApplicationContext& appCtx) override
    {
        // read arguments and set up resources here
        return EXIT_SUCCESS;
    }

    // called once initialization succeeded, returns when termination is requested
    std::int32_t Run(const score::cpp::stop_token& stopToken) override
    {
        while (!stopToken.stop_requested())
        {
            // do some work
        }

        return EXIT_SUCCESS;
    }
};

int main(int argc, char** argv)
{
    return score::mw::lifecycle::run_application<LifecycleApp>(argc, argv);
}

State management#

A State Manager is an application that decides which run-targets shall be active by communicating with the Launch Manager using the ILmControl interface. The State Manager is itself a managed component. Before calling activate_run_target, it must report the Running state to the Launch Manager via any of the Lifecycle Interfaces. Otherwise, the Launch Manager does not consider the State Manager ready, which can lead to errors.

Additionally, the State Manager component must have application_profile.application_type set to State_Manager.

Example#

State Manager example
/********************************************************************************
 * Copyright (c) 2026 Contributors to the Eclipse Foundation
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Apache License Version 2.0 which is available at
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * SPDX-License-Identifier: Apache-2.0
 ********************************************************************************/

#include <unistd.h>
#include <atomic>
#include <cassert>
#include <chrono>
#include <csignal>
#include <iostream>

#include "score/mw/com/runtime.h"
#include "score/string_manipulation/arguments/arguments.h"
#include <score/mw/lifecycle/ilm_control.hpp>
#include <score/mw/lifecycle/report_running.h>

using namespace std::literals::chrono_literals;

/// Used to signal if the startup run target switch was succesfull.
std::atomic<bool> startup_finished{false};

/// Used if a termination was requested from a signal.
std::atomic<bool> exitRequested{false};

void signalHandler(int signal)
{
    exitRequested = true;
}

int main(int argc, const char* argv[])
{
    // initialize com, needed if you are giving the mw_conf.json as an argument
    score::mw::com::runtime::InitializeRuntime(score::string_manipulation::GetArguments(argc, argv));

    // register signal handler and assert success
    assert(signal(SIGTERM, signalHandler));

    // report running to LM
    score::mw::lifecycle::report_running();

    // create a client
    auto client_result = score::mw::lifecycle::ILmControl::Create("StateManager/LaunchManager/Instance");
    if (!client_result.has_value())
    {
        std::cerr << "Failed to create client " << client_result.error() << std::endl;
        return EXIT_FAILURE;
    }

    // get the client from the result
    std::unique_ptr<score::mw::lifecycle::ILmControl> client = std::move(client_result).value();

    // register a method that gets called when a run target is switched
    const auto register_result = client->register_run_target_activation_callback(
        []([[maybe_unused]] score::mw::lifecycle::RunTargetActivationSource source,
           score::mw::lifecycle::RunTargetName target) {
            // signal that the startup was completed
            if (source == score::mw::lifecycle::RunTargetActivationSource::kInitialActivation)
            {
                std::cout << "Initial activation succesfull! activated run target " << target << std::endl;
                startup_finished.store(true);
            }
            else
            {
                std::cout << "Successfully activated Run Target " << target << std::endl;
            }
        });
    if (!register_result.has_value())
    {
        std::cerr << "Failed to register activation callback " << register_result.error() << std::endl;
        return EXIT_FAILURE;
    }

    while (startup_finished.load())
    {
        std::this_thread::sleep_for(100ms);
    }

    // activate a different run target
    const auto result = client->activate_run_target(score::mw::lifecycle::RunTargetName{"Running"}, true);
    if (result.has_value())
    {
        std::cout << "Successfully requested Run Target " << std::endl;
    }
    else
    {
        std::cerr << "Requesting Run Target "
                  << " failed with error: " << result.error().Message() << std::endl;
    }

    // wait until lm wants to terminate
    while (!exitRequested)
    {
        pause();
    }

    return EXIT_SUCCESS;
}