Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions score/launch_manager/src/daemon/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ cc_binary(
visibility = ["//score/launch_manager:__subpackages__"],
deps = [
"//score/launch_manager/src/daemon/src/alive_monitor",
"//score/launch_manager/src/daemon/src/common:assertion_handler",
"//score/launch_manager/src/daemon/src/common:log",
"//score/launch_manager/src/daemon/src/osal:ipc_comms",
"//score/launch_manager/src/daemon/src/process_group_manager",
Expand Down
10 changes: 10 additions & 0 deletions score/launch_manager/src/daemon/src/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@
load("@rules_cc//cc:defs.bzl", "cc_test")
load("//config:common_cc.bzl", "cc_binary_with_common_opts", "cc_library_with_common_opts")

cc_library(
name = "assertion_handler",
srcs = ["assertion_handler.cpp"],
hdrs = ["assertion_handler.hpp"],
include_prefix = "score/mw/launch_manager/common",
strip_include_prefix = "/score/launch_manager/src/daemon/src/common",
visibility = ["//score:__subpackages__"],
deps = ["@score_baselibs//score/language/futurecpp"],
)

cc_library(
name = "identifier_hash",
srcs = [
Expand Down
102 changes: 102 additions & 0 deletions score/launch_manager/src/daemon/src/common/assertion_handler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/********************************************************************************
* 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 <charconv>
#include <unistd.h>
#include <cerrno>
#include <cstdlib>
#include <cstring>

#include "score/language/futurecpp/include/score/assert.hpp"

namespace score
{
namespace lcm
{
namespace internal
{

/*
* The assertion handler may be called from any context, therefore, it must
* be async signal safe.
*
* If we encounter an error simply abort, as we are already in the assertion
* handler so there is nothing else to do.
*/

void stderr_print(const char* message)
{
auto offset = 0;
const auto length = strlen(message);

while (offset < length)
{
const auto result = write(2, &message[offset], length - offset);

if (result == -1)
{
if (errno != EINTR)
std::abort();
}
else
{
offset += result;
}
}
}

void assertion_handler(const cpp::handler_parameters& params)
{
stderr_print("Assertion failed:");

if (params.file != nullptr)
{
stderr_print("\n In: ");
stderr_print(params.file);
}

if (params.function != nullptr)
{
stderr_print("\n In: ");
stderr_print(params.function);
}

if (params.line != 0)
{
stderr_print("\n Line: ");
char buffer[5] = {0};
const auto result = std::to_chars(buffer, &buffer[3], params.line);
if (result.ec == std::errc())
stderr_print(buffer);
else
stderr_print(">9999");
}

if (params.condition != nullptr)
{
stderr_print("\n Condition: ");
stderr_print(params.condition);
}

if (params.message != nullptr)
{
stderr_print("\n ");
stderr_print(params.message);
}

stderr_print("\n");
}

} // namespace internal
} // namespace lcm
} // namespace score
30 changes: 30 additions & 0 deletions score/launch_manager/src/daemon/src/common/assertion_handler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/********************************************************************************
* 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 <cstring>

#include "score/language/futurecpp/include/score/assert.hpp"

namespace score
{
namespace lcm
{
namespace internal
{

void assertion_handler(const cpp::handler_parameters& params);

} // namespace internal
} // namespace lcm
} // namespace score
16 changes: 2 additions & 14 deletions score/launch_manager/src/daemon/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
#include <unistd.h>
#include <cstring>
#include <iostream>
#include <sstream>

#include <score/assert.hpp>

#include "score/mw/launch_manager/common/assertion_handler.hpp"
#include "score/mw/launch_manager/common/log.hpp"

#include "score/mw/launch_manager/alive_monitor/details/daemon/AliveMonitorImpl.hpp"
Expand Down Expand Up @@ -161,19 +161,7 @@ int main(int argc, const char* argv[])
int main([[maybe_unused]] int argc, [[maybe_unused]] const char* argv[])
{
#endif
score::cpp::set_assertion_handler([](const score::cpp::handler_parameters& params) {
std::ostringstream msg;
msg << "Assertion failed: " << (params.condition != nullptr ? params.condition : "")
<< "\n Location: " << (params.file != nullptr ? params.file : "?") << ":" << params.line
<< " (" << (params.function != nullptr ? params.function : "?") << ")";
if (params.message != nullptr)
{
msg << "\n Message: " << params.message;
}
msg << "\n";
std::cerr << msg.str();
});

score::cpp::set_assertion_handler(assertion_handler);

// reserve files descriptor osal::IpcCommsSync::sync_fd (fd3) and
// osal::IpcCommsSync::control_client_handler_nudge_fd (fd4) for communication tpyes: kNoComms !fd3 & !fd4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,23 @@
# *******************************************************************************
load("@rules_cc//cc:defs.bzl", "cc_library")

cc_library(
name = "configure_process",
srcs = ["configure_process.cpp"],
hdrs = ["configure_process.hpp"],
include_prefix = "score/mw/launch_manager/process_group_manager/details",
strip_include_prefix = "/score/launch_manager/src/daemon/src/process_group_manager/details",
visibility = ["//score/launch_manager/src/daemon/src/process_group_manager:__pkg__"],
deps = [
"//score/launch_manager/src/daemon/src/osal:return_types",
"//score/launch_manager/src/daemon/src/osal:security_policy",
"//score/launch_manager/src/daemon/src/osal:set_affinity",
"//score/launch_manager/src/daemon/src/osal:set_groups",
"//score/launch_manager/src/daemon/src/osal:sys_exit",
"//score/launch_manager/src/daemon/src/process_group_manager:iprocess",
],
)

cc_library(
name = "process_info_node",
hdrs = ["process_info_node.hpp"],
Expand Down Expand Up @@ -108,13 +125,10 @@ cc_library(
srcs = ["process_launcher.cpp"],
visibility = ["//score/launch_manager/src/daemon/src/process_group_manager:__pkg__"],
deps = [
":configure_process",
"//score/launch_manager/src/daemon/src/common:log",
"//score/launch_manager/src/daemon/src/control:control_client_channel",
"//score/launch_manager/src/daemon/src/osal:ipc_comms",
"//score/launch_manager/src/daemon/src/osal:security_policy",
"//score/launch_manager/src/daemon/src/osal:set_affinity",
"//score/launch_manager/src/daemon/src/osal:set_groups",
"//score/launch_manager/src/daemon/src/osal:sys_exit",
"//score/launch_manager/src/daemon/src/process_group_manager:iprocess",
],
)
Expand All @@ -135,6 +149,7 @@ cc_library(
}),
visibility = ["//score/launch_manager/src/daemon/src/process_group_manager:__pkg__"],
deps = [
":configure_process",
":graph",
":os_handler",
":process_info_node",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/********************************************************************************
* 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 <limits.h>

#include <libgen.h>
#include <sys/resource.h>
#include <cerrno>
#include <cstring>
#include <string>
#include <string_view>

#include "score/launch_manager/src/daemon/src/osal/return_types.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/process_group_manager/iprocess.hpp"

/*
* In this file only async signal safe functions can be used, as these functions
* are used between `fork` and `execve`.
*
* This is necessary because `fork` only copies the current thread, so any locks
* which were held at that time will never be released. See `man 2 fork`.
*
* A big implication is that we must use assertions rather than log messages,
* and the assertion handler itself must follow these rules.
*/

namespace score
{

namespace lcm
{

namespace internal
{

void setLimit(const int resource, const std::size_t amount, const std::string_view rlimit_name)
{
if (amount == 0)
return;

const struct rlimit limit{
.rlim_cur = amount,
.rlim_max = amount,
};

const auto result = ::setrlimit(resource, &limit);
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(

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 we have the same async signal safe issue with the assert message, as the string concatenation may cause heap allocation which is not async signal safe

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We could remove the extra info and just use strerror as the message

@danth danth Jul 16, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Update: strerror is not async signal safe either, there is strerrordesc_np but it's not standard

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.

bazel assort macros terminate the process using std::abort(). Is this safe to terminate the process this way btw. fork() and execve()?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

abort is async signal safe in POSIX, not sure if std::abort is doing anything extra?

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 here it is not so much about the signal safety, but other considerations what is being cleaned up: https://www.unixguide.net/unix/programming/1.1.3.shtml

Not sure though exactly what is the difference btw. _exit and std::abort, but If I remember correctly using _exit() is the recommended way to terminate a process btw. fork and execve in case of error

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Some more info- std::abort and abort are one and the same. abort does no more cleanup than _exit. The main difference is that abort sends SIGABRT which triggers a core dump whereas _exit ends the process normally

result != -1, ("Failed to set rlimit " + std::string(rlimit_name) + ": " + std::strerror(errno)).c_str());
}

void setSchedulingAndSecurity(const osal::OsalConfig& config)
{
int result; // Used for return value from system calls

// Set process group id to be equal to the pid
// setpgid will fail if called by a session leader (which LCMd is), so skip
if (config.comms_type_ != osal::CommsType::kLaunchManager)
{
result = setpgid(0, getpid());
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(result == 0, std::strerror(errno));
Comment thread
danth marked this conversation as resolved.
}

// Set scheduling policy with sched_setscheduler
sched_param sch_param{};

sch_param.sched_priority = config.scheduling_priority_;

// TODO: Clamping should be done when the config is loaded
Comment thread
danth marked this conversation as resolved.
// https://github.com/eclipse-score/lifecycle/issues/304
if (sch_param.sched_priority < sched_get_priority_min(config.scheduling_policy_))
{
sch_param.sched_priority = sched_get_priority_min(config.scheduling_policy_);
}
else if (sch_param.sched_priority > sched_get_priority_max(config.scheduling_policy_))
{
sch_param.sched_priority = sched_get_priority_max(config.scheduling_policy_);
}

result = sched_setscheduler(0, config.scheduling_policy_, &sch_param);
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(result != -1, std::strerror(errno));

result = osal::setaffinity(config.cpu_mask_);
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(result != -1, std::strerror(errno));

result = setgid(config.gid_);
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(result != -1, std::strerror(errno));

// Note: the type of the first parameter of setgroups() differs in Linux and QNX, so we use osal
const auto gids_size = config.supplementary_gids_.size();
if (gids_size > 0)
{
const auto gids_data = config.supplementary_gids_.data();
result = osal::setgroups(gids_size, gids_data);
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(result != -1, std::strerror(errno));
}

result = setuid(config.uid_);
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(result != -1, std::strerror(errno));
}

void changeCurrentWorkingDirectory(const osal::OsalConfig& config)
{
// 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[PATH_MAX + 1U] = {0};

// TODO: This should be validated when the config is loaded
Comment thread
NicolasFussberger marked this conversation as resolved.
// https://github.com/eclipse-score/lifecycle/issues/304
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(config.executable_path_.size() < PATH_MAX,
"Executable path is too long");

const auto result = chdir(dirname(strncpy(path_copy, config.executable_path_.c_str(), PATH_MAX)));
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(result != -1, std::strerror(errno));
}

void implementMemoryResourceLimits(const osal::OsalConfig& config)
{
setLimit(RLIMIT_DATA, config.resource_limits_.data_, "RLIMIT_DATA");
setLimit(RLIMIT_AS, config.resource_limits_.as_, "RLIMIT_AS");
setLimit(RLIMIT_STACK, config.resource_limits_.stack_, "RLIMIT_STACK");

// Note about cpu limit:
// Using setrlimit, this imposes a maximum time that a process will run for, which might not be
// what you intend? Probably you'll want a maximum time in a time-slice, but you don't get that
// with limits set by setrlimit...
setLimit(RLIMIT_CPU, config.resource_limits_.cpu_, "RLIMIT_CPU");
}

void changeSecurityPolicy(const osal::OsalConfig& config)
{
if (config.security_policy_ == "")
return;

const auto result = osal::setSecurityPolicy(config.security_policy_.c_str());
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(
result == 0,
("Failed to set security policy " + config.security_policy_ + ": " + std::strerror(errno)).c_str());
}

} // namespace internal

} // namespace lcm

} // namespace score
Loading
Loading