Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test_and_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
contents: read
pull-requests: read
with:
bazel-target: 'test --lockfile_mode=error //score/... //tests/... --config=x86_64-linux'
bazel-target: 'test --lockfile_mode=error //score/... //tests/... //scripts/... --config=x86_64-linux'
upload-name: 'bazel-testlogs'
packages: 'fakechroot'
build-docs:
Expand Down
17 changes: 13 additions & 4 deletions patches/BUILD
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
exports_files(["hedron_compile_commands_env_vars.patch"])
# *******************************************************************************
# 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
# *******************************************************************************

exports_files(["score_bazel_tools.patch"])

exports_files(["score_bazel_tools_python.patch"])
exports_files(glob(["*.patch"]))
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ Component
``run_target`` set to ``fallback_run_target``.
* The ``ready_timeout`` is used as the timeout until process state Running is
reached, even in case the ReadyCondition is ``process_state:Terminated``.
* The parameter ``deployment_config/working_dir`` is currently not supported
and is ignored.


Run target
----------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ table Process {
uid: uint (id:4);
gid: uint (id:5);
path: string (id:6);
working_dir: string (id:11);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is adding this to the old lm_flatcfg.fbs however we are converting to the new schema in score/launch_manager/src/daemon/src/configuration/details/new_lm_flatcfg.fbs I think we can make this change just for the new config and not edit the old.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing for the loader code in score/launch_manager/src/daemon/src/configuration/

security_policy_details: string (id:7);
executable_reporting_behavior: ExecutionStateReportingBehaviorEnum (id:8);
startup_config: [ProcessStartupConfig] (id:9);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ void ConfigurationAdapter::fillStartupConfigFromDeployment(
const auto& props = comp.component_properties;

startup.executable_path_ = deploy.bin_dir + "/" + props.binary_name;
startup.working_dir_ = deploy.working_dir;
startup.short_name_ = comp.name;

startup.uid_ = deploy.sandbox.uid;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -521,8 +521,10 @@ bool ConfigurationManager::parseProcessConfigurations(const Process* node)
setSchedulingParameters(*node, *startup_config_node, instance);
// Set executable path from node's path
instance.startup_config_.executable_path_ = getStringFromFlatBuffer(node->path());
instance.startup_config_.working_dir_ = getStringFromFlatBuffer(node->working_dir());
LM_LOG_DEBUG() << "parseProcessConfigurations: Process index:" << instance.process_number_
<< "executable_path_:" << instance.startup_config_.executable_path_;
<< "executable_path_:" << instance.startup_config_.executable_path_
<< " working_dir_:" << instance.startup_config_.working_dir_;

instance.startup_config_.short_name_ = node->identifier() ? node->identifier()->c_str() : "Unknown";
instance.startup_config_.uid_ = node->uid() & 0x7FFFFFFFU;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@
#include <limits.h>
#include <signal.h>

#include "score/mw/launch_manager/process_group_manager/iprocess.hpp"
#include "score/mw/launch_manager/control/control_client_channel.hpp"
#include "score/mw/launch_manager/common/log.hpp"
#include "score/mw/launch_manager/control/control_client_channel.hpp"
#include "score/mw/launch_manager/osal/ipc_comms.hpp"
#include "score/mw/launch_manager/osal/security_policy.hpp"
#include "score/mw/launch_manager/osal/set_affinity.hpp"
#include "score/mw/launch_manager/osal/set_groups.hpp"
#include "score/mw/launch_manager/osal/sys_exit.hpp"
#include "score/mw/launch_manager/process_group_manager/iprocess.hpp"
#include <cerrno>
#include <csignal>
#include <cstdio>
Expand Down Expand Up @@ -133,22 +133,11 @@ void handleComms(score::lcm::internal::osal::ChildProcessConfig& param)

void changeCurrentWorkingDirectory(const score::lcm::internal::osal::OsalConfig& config)
{
// Change current working directory to the same as the executable
constexpr size_t string_size = static_cast<size_t>(PATH_MAX);
// Notice that this next static variable is duplicated by the fork() and so does not need
// any protection by a mutex although at first sight you may think it could need one.
static char path_copy[string_size + 1U] = {0};

if (config.executable_path_.size() >= string_size)
{
LM_LOG_ERROR() << "[New process] executable path longer than" << string_size
<< "chars:" << config.executable_path_;
sysexit(EXIT_FAILURE);
}

if (-1 == chdir(dirname(strncpy(path_copy, config.executable_path_.c_str(), string_size))))
// working_dir_ is set by python configuration generator in lifecycle_config.py, so it should always be valid.
// If not, chdir will fail anyway and we will log an error and exit.
if (-1 == chdir(config.working_dir_.c_str()))
{
LM_LOG_ERROR() << "[New process] chdir(" << config.executable_path_
LM_LOG_ERROR() << "[New process] chdir(" << config.working_dir_
<< ") failed:" << score::lcm::internal::errno_message(errno);
sysexit(EXIT_FAILURE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ struct OsalConfig
int32_t scheduling_policy_; ///< Scheduling policy defined for this process
int32_t scheduling_priority_; ///< Scheduling priority for this process
OsalLimits resource_limits_; ///< Resource limits for this process
std::string working_dir_{}; ///< Working directory for the process
};

/// @brief Struct to hold configuration parameters for the child process.
Expand Down
1 change: 1 addition & 0 deletions scripts/config_mapping/integration_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
script_dir.parent.parent
/ "score"
/ "launch_manager"
/ "src"
/ "daemon"
/ "src"
/ "configuration"
Expand Down
24 changes: 19 additions & 5 deletions scripts/config_mapping/lifecycle_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
"shutdown_timeout": 0.5,
"environmental_variables": {},
"bin_dir": "/opt",
"working_dir": "/tmp",
"ready_recovery_action": {
"restart": {
"number_of_attempts": 0,
Expand Down Expand Up @@ -108,6 +107,14 @@ def get_recovery_process_group_state(config):
def sec_to_ms(sec: float) -> int:
return int(sec * 1000)

def get_working_dir(deployment_config):
"""
Get the working directory for a component. If not specified, default to the bin_dir.
"""
return deployment_config.get(
"working_dir", deployment_config["bin_dir"]
)


def preprocess_defaults(global_defaults, config):
"""
Expand Down Expand Up @@ -185,9 +192,13 @@ def dict_merge_recursive(dict_a, dict_b):
merged_defaults["watchdog"], config.get("watchdog", {})
)

new_config["initial_run_target"] = config.get(
"initial_run_target", merged_defaults["initial_run_target"]
)
# Only emit initial_run_target when it can be derived from the input config
# or the defaults. This keeps preprocess_defaults usable with minimal
# defaults that don't define run-target keys.
if "initial_run_target" in config or "initial_run_target" in merged_defaults:
new_config["initial_run_target"] = config.get(
"initial_run_target", merged_defaults.get("initial_run_target")
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this change it redundant. There is a validation that ensures that initial_run_target must always be defined (to Startup state)


if "fallback_run_target" in config:
fallback_defaults = {
Expand Down Expand Up @@ -289,7 +300,9 @@ def gen_config(output_dir, config, input_filename, schema_version=None):
"ready_timeout": depl_cfg["ready_timeout"],
"shutdown_timeout": depl_cfg["shutdown_timeout"],
"bin_dir": depl_cfg["bin_dir"],
"working_dir": depl_cfg["working_dir"],
# Default the working directory to bin_dir (the directory the
# executable lives in) when not set explicitly.
"working_dir": get_working_dir(depl_cfg),
"sandbox": sandbox_out,
}

Expand Down Expand Up @@ -668,6 +681,7 @@ def get_terminating_behavior(component_config):
process["number_of_restart_attempts"] = component_config["deployment_config"][
"ready_recovery_action"
]["restart"]["number_of_attempts"]
process["working_dir"] = get_working_dir(component_config["deployment_config"])

match component_config["component_properties"]["application_profile"][
"application_type"
Expand Down
1 change: 0 additions & 1 deletion scripts/config_mapping/tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ score_py_pytest(
"//score/launch_manager/src/daemon/src/configuration/config_schema:launch_manager.schema.json",
"//scripts/config_mapping:lifecycle_config.py",
],
tags = ["manual"],
deps = [
requirement("pytest"),
requirement("jsonschema"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
],
"security_policy_details": "policy_name",
"number_of_restart_attempts": 1,
"working_dir": "/opt/scripts",
"executable_reporting_behavior": "DoesNotReportExecutionState",
"startup_config": [
{
Expand Down Expand Up @@ -84,6 +85,7 @@
],
"security_policy_details": "policy_name",
"number_of_restart_attempts": 1,
"working_dir": "/opt/apps/dlt-daemon",
"executable_reporting_behavior": "DoesNotReportExecutionState",
"startup_config": [
{
Expand Down Expand Up @@ -152,6 +154,7 @@
],
"security_policy_details": "policy_name",
"number_of_restart_attempts": 1,
"working_dir": "/opt/apps/someip",
"executable_reporting_behavior": "ReportsExecutionState",
"startup_config": [
{
Expand Down Expand Up @@ -219,6 +222,7 @@
],
"security_policy_details": "policy_name",
"number_of_restart_attempts": 1,
"working_dir": "/opt/apps/test_app1",
"executable_reporting_behavior": "ReportsExecutionState",
"startup_config": [
{
Expand Down Expand Up @@ -295,6 +299,7 @@
],
"security_policy_details": "policy_name",
"number_of_restart_attempts": 1,
"working_dir": "/opt/apps/state_manager",
"executable_reporting_behavior": "ReportsExecutionState",
"function_cluster_affiliation": "STATE_MANAGEMENT",
"startup_config": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"EMPTY_GLOBAL_ENV_VAR": ""
},
"bin_dir": "/opt",
"working_dir": "/tmp",
"ready_recovery_action": {
"restart": {
"number_of_attempts": 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
],
"security_policy_details": "policy_name_2",
"number_of_restart_attempts": 1,
"working_dir": "/opt/apps/test_app2",
"executable_reporting_behavior": "ReportsExecutionState",
"startup_config": [
{
Expand Down Expand Up @@ -92,6 +93,7 @@
],
"security_policy_details": "policy_name",
"number_of_restart_attempts": 1,
"working_dir": "/tmp",
"executable_reporting_behavior": "DoesNotReportExecutionState",
"startup_config": [
{
Expand Down Expand Up @@ -161,6 +163,7 @@
],
"security_policy_details": "policy_name",
"number_of_restart_attempts": 1,
"working_dir": "/tmp",
"executable_reporting_behavior": "DoesNotReportExecutionState",
"startup_config": [
{
Expand Down Expand Up @@ -229,6 +232,7 @@
],
"security_policy_details": "policy_name",
"number_of_restart_attempts": 1,
"working_dir": "/tmp",
"executable_reporting_behavior": "ReportsExecutionState",
"startup_config": [
{
Expand Down Expand Up @@ -296,6 +300,7 @@
],
"security_policy_details": "policy_name",
"number_of_restart_attempts": 1,
"working_dir": "/tmp",
"executable_reporting_behavior": "ReportsExecutionState",
"startup_config": [
{
Expand Down Expand Up @@ -372,6 +377,7 @@
],
"security_policy_details": "policy_name",
"number_of_restart_attempts": 1,
"working_dir": "/tmp",
"executable_reporting_behavior": "ReportsExecutionState",
"function_cluster_affiliation": "STATE_MANAGEMENT",
"startup_config": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@
"scheduling_priority": 99,
"max_memory_usage": 2048,
"max_cpu_usage": 50
}
},
"working_dir": "/opt/apps/test_app2"
}
},
"setup_filesystem_sh": {
Expand Down
63 changes: 63 additions & 0 deletions tests/integration/sandbox_options/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# *******************************************************************************
# 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
# *******************************************************************************
load("@rules_cc//cc:defs.bzl", "cc_binary")
load("//tests/utils/bazel:integration.bzl", "integration_test")

cc_binary(
name = "control_daemon_mock",
srcs = ["control_daemon_mock.cpp"],
data = [],
deps = [
"//score/launch_manager:control_cc",
"//score/launch_manager:lifecycle_cc",
"//tests/utils/test_helper",
"@googletest//:gtest_main",
],
)

# Two binaries are built from the same source. Each verifies the sandbox options passed to it on
# the command line (see sandbox_options.json), and derives its GTest XML result file name from its
# own executable name so the results do not collide.
cc_binary(
name = "sandbox_options_process_a",
srcs = ["sandbox_options_process.cpp"],
deps = [
"//score/launch_manager:control_cc",
"//score/launch_manager:lifecycle_cc",
"//tests/utils/test_helper",
"@googletest//:gtest_main",
],
)

cc_binary(
name = "sandbox_options_process_b",
srcs = ["sandbox_options_process.cpp"],
deps = [
"//score/launch_manager:control_cc",
"//score/launch_manager:lifecycle_cc",
"//tests/utils/test_helper",
"@googletest//:gtest_main",
],
)

integration_test(
name = "sandbox_options",
srcs = ["sandbox_options.py"],
binaries = [
":control_daemon_mock",
":sandbox_options_process_a",
":sandbox_options_process_b",
"//score/launch_manager",
],
config = ":sandbox_options.json",
)
Loading
Loading