diff --git a/examples/c_supervised_app/BUILD b/examples/c_supervised_app/BUILD new file mode 100644 index 000000000..97deed6d6 --- /dev/null +++ b/examples/c_supervised_app/BUILD @@ -0,0 +1,34 @@ +# ******************************************************************************* +# 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") + +cc_binary( + name = "c_supervised_app", + srcs = [ + "main.c", + ], + linkopts = select({ + "@platforms//os:qnx": [ + "-lsocket", + ], + "@platforms//os:linux": [ + "-lpthread", + "-lrt", + ], + }), + visibility = ["//visibility:public"], + deps = [ + "//score/launch_manager:alive_cc", + "//score/launch_manager:lifecycle_cc", + ], +) diff --git a/examples/c_supervised_app/main.c b/examples/c_supervised_app/main.c new file mode 100644 index 000000000..d906ff40b --- /dev/null +++ b/examples/c_supervised_app/main.c @@ -0,0 +1,59 @@ +/******************************************************************************** + * 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 +#include +#include +#include + +#include +#include + +static volatile sig_atomic_t exit_requested = 0; + +static void signal_handler(int signal) +{ + if (signal == SIGINT || signal == SIGTERM) + { + exit_requested = 1; + } +} + +int main(void) +{ + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + void* alive = score_lcm_alive_initialize("/example/instance"); + if (alive == NULL) + { + fprintf(stderr, "Failed to initialize alive instance\n"); + return EXIT_FAILURE; + } + + if (score_lcm_ReportRunning() != 0) + { + fprintf(stderr, "Failed to report running state\n"); + score_lcm_alive_deinitialize(alive); + return EXIT_FAILURE; + } + + while (!exit_requested) + { + score_lcm_alive_report_alive(alive); + usleep(50000); + } + + score_lcm_alive_deinitialize(alive); + return EXIT_SUCCESS; +} diff --git a/examples/demo_verification/BUILD b/examples/demo_verification/BUILD index 91a0efdb6..540375b3b 100644 --- a/examples/demo_verification/BUILD +++ b/examples/demo_verification/BUILD @@ -24,6 +24,7 @@ launch_manager_config( pkg_files( name = "examples_main_files", srcs = [ + "//examples/c_supervised_app", "//examples/control_application:control_daemon", "//examples/control_application:lmcontrol", "//examples/cpp_lifecycle_app", diff --git a/examples/demo_verification/lifecycle_demo_test.json b/examples/demo_verification/lifecycle_demo_test.json index bd0a92645..0ad84cdb4 100644 --- a/examples/demo_verification/lifecycle_demo_test.json +++ b/examples/demo_verification/lifecycle_demo_test.json @@ -94,6 +94,20 @@ } } }, + "c_supervised_app": { + "component_properties": { + "binary_name": "c_supervised_app", + "application_profile": { + "application_type": "Reporting_And_Supervised" + } + }, + "deployment_config": { + "environmental_variables": { + "PROCESSIDENTIFIER": "c_supervised_app", + "IDENTIFIER": "c_supervised_app" + } + } + }, "lifecycle_app": { "component_properties": { "binary_name": "cpp_lifecycle_app" @@ -121,6 +135,7 @@ "control_daemon", "cpp_supervised_app", "rust_supervised_app", + "c_supervised_app", "lifecycle_app" ], "recovery_action": { diff --git a/examples/demo_verification/test_examples.py b/examples/demo_verification/test_examples.py index 1a1e28d92..ea617e149 100644 --- a/examples/demo_verification/test_examples.py +++ b/examples/demo_verification/test_examples.py @@ -15,7 +15,12 @@ from tests.utils.testing_utils.setup_test import setup_test from attribute_plugin import add_test_properties -_DEMO_APPS = ("cpp_supervised_app", "rust_supervised_app", "cpp_lifecycle_app") +_DEMO_APPS = ( + "cpp_supervised_app", + "rust_supervised_app", + "cpp_lifecycle_app", + "c_supervised_app", +) def _assert_running(target, *binaries): diff --git a/score/launch_manager/src/alive/src/alive.cpp b/score/launch_manager/src/alive/src/alive.cpp index 53eec5903..8f825dac8 100644 --- a/score/launch_manager/src/alive/src/alive.cpp +++ b/score/launch_manager/src/alive/src/alive.cpp @@ -14,8 +14,8 @@ #include "score/mw/lifecycle/alive.h" #include "score/mw/launch_manager/alive_monitor/details/AliveImpl.h" -#include #include +#include // The public API is only sending alive notification. No need to support different checkpoints. static constexpr std::uint32_t kDefaultCheckpointId{1U}; @@ -23,10 +23,7 @@ static constexpr std::uint32_t kDefaultCheckpointId{1U}; namespace score::mw::lifecycle { -Alive::Alive(const std::string_view& instance) noexcept(false) : - aliveImplPtr(std::make_unique(instance)) -{ -} +Alive::Alive(const std::string_view& instance) noexcept(false) : aliveImplPtr(std::make_unique(instance)) {} Alive::Alive(Alive&& se) noexcept = default; @@ -49,41 +46,47 @@ void Alive::ReportFailure() const noexcept } // namespace score::mw::lifecycle - #ifdef __cplusplus extern "C" { #endif -void* score_lcm_alive_initialize(const char* instanceSpecifier) noexcept { - if(instanceSpecifier == nullptr) { +void* score_lcm_alive_initialize(const char* instanceSpecifier) +{ + if (instanceSpecifier == nullptr) + { return nullptr; } - try { + try + { auto* alivePtr = new score::mw::lifecycle::Alive(instanceSpecifier); return static_cast(alivePtr); - } catch (...) { + } + catch (...) + { return nullptr; } } -void score_lcm_alive_deinitialize(void* instance) noexcept { +void score_lcm_alive_deinitialize(void* instance) +{ SCORE_LANGUAGE_FUTURECPP_PRECONDITION(instance != nullptr); auto* alivePtr = static_cast(instance); delete alivePtr; } -void score_lcm_alive_report_alive(void* instance) noexcept { +void score_lcm_alive_report_alive(void* instance) +{ SCORE_LANGUAGE_FUTURECPP_PRECONDITION(instance != nullptr); static_cast(instance)->ReportAlive(); } -void score_lcm_alive_report_failure(void* instance) noexcept { +void score_lcm_alive_report_failure(void* instance) +{ SCORE_LANGUAGE_FUTURECPP_PRECONDITION(instance != nullptr); static_cast(instance)->ReportFailure(); } - #ifdef __cplusplus } #endif diff --git a/score/launch_manager/src/alive/src/alive.h b/score/launch_manager/src/alive/src/alive.h index 376cf1bda..8e6daf967 100644 --- a/score/launch_manager/src/alive/src/alive.h +++ b/score/launch_manager/src/alive/src/alive.h @@ -14,6 +14,7 @@ #ifndef SCORE_LCM_ALIVE_H_ #define SCORE_LCM_ALIVE_H_ +#ifdef __cplusplus #include #include #include @@ -26,14 +27,14 @@ class AliveImpl; /// @brief Alive API for reporting alive notifications to the launch manager. /// An alive notification indicates that the component is still active and functioning correctly. -/// The launch manager is configured with an expected alive notification interval, -/// and if it does not receive an alive notification within that interval, +/// The launch manager is configured with an expected alive notification interval, +/// and if it does not receive an alive notification within that interval, /// it executes the configured recovery action. /// /// Each process may only use a single Alive instance. class Alive { -public: + public: /// @brief Creation of an Alive. /// @param [in] instance Instance specifier (currently unused) /// @throws std::runtime_error if the configured IPC channel to connect to launch manager is not existing @@ -67,10 +68,38 @@ class Alive /// @note Not Implemented. This method currently does nothing. void ReportFailure() const noexcept; -private: + private: /// @brief Unique pointer to implementation class of Alive std::unique_ptr aliveImplPtr; }; } // namespace score::mw::lifecycle + +extern "C" { +#endif /* __cplusplus */ + +// These prototypes are required to support applications written in C, which cannot use the C++ namespaces and classes. + +/// @brief Initialize an Alive instance. +/// @param [in] instanceSpecifier Instance specifier string (must not be NULL) +/// @return Opaque pointer to the Alive instance, or NULL on failure +void* score_lcm_alive_initialize(const char* instanceSpecifier); + +/// @brief Deinitialize (destroy) an Alive instance. +/// @param [in] instance Opaque pointer previously returned by score_lcm_alive_initialize (must not be NULL) +void score_lcm_alive_deinitialize(void* instance); + +/// @brief Report an alive notification. +/// @param [in] instance Opaque pointer previously returned by score_lcm_alive_initialize (must not be NULL) +void score_lcm_alive_report_alive(void* instance); + +/// @brief Report a direct failure. +/// @param [in] instance Opaque pointer previously returned by score_lcm_alive_initialize (must not be NULL) +/// @note Not Implemented. This method currently does nothing. +void score_lcm_alive_report_failure(void* instance); + +#ifdef __cplusplus +} +#endif + #endif // SCORE_LCM_ALIVE_H_ diff --git a/score/launch_manager/src/lifecycle_client/src/report_running.h b/score/launch_manager/src/lifecycle_client/src/report_running.h index 6a37c1faf..ec8bfdce1 100644 --- a/score/launch_manager/src/lifecycle_client/src/report_running.h +++ b/score/launch_manager/src/lifecycle_client/src/report_running.h @@ -14,9 +14,16 @@ #ifndef SCORE_MW_LIFECYCLE_REPORT_RUNNING_H_ #define SCORE_MW_LIFECYCLE_REPORT_RUNNING_H_ -namespace score { -namespace mw { -namespace lifecycle { +#include + +#ifdef __cplusplus + +namespace score +{ +namespace mw +{ +namespace lifecycle +{ /// @brief Signals to the Launch Manager that this process has finished /// initialization and is now entering its Running state. @@ -36,8 +43,21 @@ namespace lifecycle { /// shutdown accordingly. void report_running() noexcept; -} // namespace lifecycle -} // namespace mw -} // namespace score +} // namespace lifecycle +} // namespace mw +} // namespace score + +extern "C" { +#endif /* __cplusplus */ + +// This prototype is required to support applications written in C, which cannot use the C++ namespaces and classes. + +/// @brief Signals to the Launch Manager that this process has finished initialization and is now running. +/// @return 0 on success, -1 on failure +int8_t score_lcm_ReportRunning(void); + +#ifdef __cplusplus +} +#endif -#endif \ No newline at end of file +#endif /* SCORE_MW_LIFECYCLE_REPORT_RUNNING_H_ */