From d72d06692362a9b660da54b0a2e0374e14a1f15b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Thu, 13 Jun 2024 12:54:35 +0000 Subject: [PATCH 01/42] infra for snitch lib --- libs/CMakeLists.txt | 1 + libs/snitch/CMakeLists.txt | 35 ++++++++++++++++++ libs/snitch/inc/public/tfc/snitch.hpp | 37 ++++++++++++++++++++ libs/snitch/inc/public/tfc/snitch/common.hpp | 14 ++++++++ libs/snitch/src/snitch.cpp | 2 ++ libs/snitch/tests/CMakeLists.txt | 5 +++ libs/snitch/tests/snitch_test.cpp | 20 +++++++++++ 7 files changed, 114 insertions(+) create mode 100644 libs/snitch/CMakeLists.txt create mode 100644 libs/snitch/inc/public/tfc/snitch.hpp create mode 100644 libs/snitch/inc/public/tfc/snitch/common.hpp create mode 100644 libs/snitch/src/snitch.cpp create mode 100644 libs/snitch/tests/CMakeLists.txt create mode 100644 libs/snitch/tests/snitch_test.cpp diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index af4130f9f4..556a254bb9 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -10,6 +10,7 @@ add_subdirectory(soem_interface) add_subdirectory(operation_mode) add_subdirectory(mqtt-sparkplug) add_subdirectory(motor) +add_subdirectory(snitch) add_library(tfc INTERFACE) add_library(tfc::tfc ALIAS tfc) diff --git a/libs/snitch/CMakeLists.txt b/libs/snitch/CMakeLists.txt new file mode 100644 index 0000000000..a69fc018ee --- /dev/null +++ b/libs/snitch/CMakeLists.txt @@ -0,0 +1,35 @@ +project(snitch) + +add_library(snitch + src/snitch.cpp +) +add_library(tfc::snitch ALIAS snitch) +target_include_directories(snitch + PUBLIC + $ + $ + PRIVATE + $ +) + +find_package(Boost REQUIRED COMPONENTS ) +find_package(fmt CONFIG REQUIRED) + +target_link_libraries(snitch + PUBLIC + fmt::fmt + Boost::boost + tfc::stx + PRIVATE + tfc::logger + tfc::base +) + +add_library_to_docs(tfc::snitch) + +if (BUILD_TESTING) + add_subdirectory(tests) +endif () + +include(tfc_install) +tfc_install_lib(snitch) diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp new file mode 100644 index 0000000000..3bc8b7edb4 --- /dev/null +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#include +#include + +namespace tfc::snitch { + +struct variance { + bool requires_acknowledgement{}; + level lvl{ level::unknown }; +}; + +template +class alarm { +public: + alarm(std::string_view description, std::string_view details) {} + + template + requires (var.requires_acknowledgement) + void on_acknowledgement(callback_t&& callback) { + + } + + +}; + +using info = alarm<{ .requires_acknowledgement = false, .lvl = level::info }>; +using warning = alarm<{ .requires_acknowledgement = false, .lvl = level::warning }>; +using warning_latched = alarm<{ .requires_acknowledgement = true, .lvl = level::warning }>; +using warning_ack = warning_latched; +using error = alarm<{ .requires_acknowledgement = false, .lvl = level::error }>; + + +} // namespace tfc::snitch diff --git a/libs/snitch/inc/public/tfc/snitch/common.hpp b/libs/snitch/inc/public/tfc/snitch/common.hpp new file mode 100644 index 0000000000..4536ba3ba3 --- /dev/null +++ b/libs/snitch/inc/public/tfc/snitch/common.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace tfc::snitch { + +enum struct level : std::uint8_t { + unknown = 0, + info, + warning, + error, +}; + +} // namespace tfc::snitch diff --git a/libs/snitch/src/snitch.cpp b/libs/snitch/src/snitch.cpp new file mode 100644 index 0000000000..bfaf531b1f --- /dev/null +++ b/libs/snitch/src/snitch.cpp @@ -0,0 +1,2 @@ + +#include diff --git a/libs/snitch/tests/CMakeLists.txt b/libs/snitch/tests/CMakeLists.txt new file mode 100644 index 0000000000..bb5139d5e6 --- /dev/null +++ b/libs/snitch/tests/CMakeLists.txt @@ -0,0 +1,5 @@ +find_package(ut CONFIG REQUIRED) + +add_executable(snitch_test snitch_test.cpp) +target_link_libraries(snitch_test PRIVATE Boost::ut tfc::snitch tfc::base) +add_test(NAME snitch_test COMMAND snitch_test) diff --git a/libs/snitch/tests/snitch_test.cpp b/libs/snitch/tests/snitch_test.cpp new file mode 100644 index 0000000000..96daf8fdfc --- /dev/null +++ b/libs/snitch/tests/snitch_test.cpp @@ -0,0 +1,20 @@ +#include +#include + +#include +#include + +using std::string_view_literals::operator""sv; + +auto main(int argc, char** argv) -> int { + using boost::ut::operator""_test; + using boost::ut::expect; + + tfc::base::init(argc, argv); + + "snitches"_test = [] { + tfc::snitch::warning_ack warn("info", "info"); + warn.on_acknowledgement([]{}); + + }; +} From 2e9ae5dd679bc8a8020906f2fa5e6c7f86bf68c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Thu, 13 Jun 2024 15:13:39 +0000 Subject: [PATCH 02/42] infra for snitch lib --- libs/snitch/inc/public/tfc/snitch.hpp | 35 +++++++++++++++++++++------ libs/snitch/tests/snitch_test.cpp | 5 ++-- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index 3bc8b7edb4..ba47eeb39b 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -3,7 +3,10 @@ #include #include +#include + #include +#include #include namespace tfc::snitch { @@ -13,25 +16,41 @@ struct variance { level lvl{ level::unknown }; }; -template +template +concept named_arg = requires(T t) { + { t.name } -> std::convertible_to; + { t.value }; +}; + +template class alarm { public: - alarm(std::string_view description, std::string_view details) {} + /// \example alarm<{}, "short desc", "long desc"> warn(fmt::arg("key", "value"), fmt::arg("key2", 1)); + alarm(named_arg auto&& ... default_args) { + // todo + } template requires (var.requires_acknowledgement) void on_acknowledgement(callback_t&& callback) { - } + + + }; -using info = alarm<{ .requires_acknowledgement = false, .lvl = level::info }>; -using warning = alarm<{ .requires_acknowledgement = false, .lvl = level::warning }>; -using warning_latched = alarm<{ .requires_acknowledgement = true, .lvl = level::warning }>; -using warning_ack = warning_latched; -using error = alarm<{ .requires_acknowledgement = false, .lvl = level::error }>; +template +using info = alarm<{ .requires_acknowledgement = false, .lvl = level::info }, description, details>; +template +using warning = alarm<{ .requires_acknowledgement = false, .lvl = level::warning }, description, details>; +template +using warning_latched = alarm<{ .requires_acknowledgement = true, .lvl = level::warning }, description, details>; +template +using warning_ack = warning_latched; +template +using error = alarm<{ .requires_acknowledgement = false, .lvl = level::error }, description, details>; } // namespace tfc::snitch diff --git a/libs/snitch/tests/snitch_test.cpp b/libs/snitch/tests/snitch_test.cpp index 96daf8fdfc..9bb3387cd5 100644 --- a/libs/snitch/tests/snitch_test.cpp +++ b/libs/snitch/tests/snitch_test.cpp @@ -13,8 +13,9 @@ auto main(int argc, char** argv) -> int { tfc::base::init(argc, argv); "snitches"_test = [] { - tfc::snitch::warning_ack warn("info", "info"); - warn.on_acknowledgement([]{}); + tfc::snitch::info<"short desc", "long desc"> info(fmt::arg("key1", "value"), fmt::arg("key2", 1)); + fmt::print("{}", 1); + // warn.on_acknowledgement([]{}); }; } From e5fbb90c834ef26748dc386e2d9dd9223965c3f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Tue, 18 Jun 2024 14:25:24 +0000 Subject: [PATCH 03/42] compile time checks for snitch client and runtime where relevant --- libs/snitch/CMakeLists.txt | 2 +- libs/snitch/inc/public/tfc/snitch.hpp | 36 +++-- .../public/tfc/snitch/format_extension.hpp | 129 ++++++++++++++++++ libs/snitch/tests/snitch_test.cpp | 38 +++++- 4 files changed, 193 insertions(+), 12 deletions(-) create mode 100644 libs/snitch/inc/public/tfc/snitch/format_extension.hpp diff --git a/libs/snitch/CMakeLists.txt b/libs/snitch/CMakeLists.txt index a69fc018ee..9d5c9bf958 100644 --- a/libs/snitch/CMakeLists.txt +++ b/libs/snitch/CMakeLists.txt @@ -20,8 +20,8 @@ target_link_libraries(snitch fmt::fmt Boost::boost tfc::stx - PRIVATE tfc::logger + PRIVATE tfc::base ) diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index ba47eeb39b..a80047e3c4 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -4,10 +4,13 @@ #include #include +#include +#include #include #include #include +#include namespace tfc::snitch { @@ -22,23 +25,40 @@ concept named_arg = requires(T t) { { t.value }; }; -template +template class alarm { public: + static constexpr std::string_view description{ in_description }; + static constexpr std::string_view details{ in_details }; + static constexpr auto description_arg_keys{ detail::arg_names() }; + static constexpr auto details_arg_keys{ detail::arg_names
() }; + static constexpr auto keys_count{ std::max(description_arg_keys.size(), details_arg_keys.size()) }; + /// \example alarm<{}, "short desc", "long desc"> warn(fmt::arg("key", "value"), fmt::arg("key2", 1)); - alarm(named_arg auto&& ... default_args) { - // todo + alarm(named_arg auto&& ... default_args) : default_values_{ std::make_pair(default_args.name, fmt::format("{}", default_args.value))... } { + static_assert(detail::check_all_arguments_named(description), "All arguments must be named"); + static_assert(detail::check_all_arguments_no_format(description), "All arguments may not have format specifiers"); + [[maybe_unused]] static constexpr int num_args = sizeof...(default_args); + static_assert(num_args <= keys_count, "Too many default arguments"); } template requires (var.requires_acknowledgement) - void on_acknowledgement(callback_t&& callback) { + void on_ack(callback_t&& callback) { } - - - - + void set(named_arg auto&& ... args) { + fmt::dynamic_format_arg_store store; + for (auto const& [key, value] : default_values_) { + store.push_back(fmt::arg(key.c_str(), value)); + } + (store.push_back(args), ...); + logger_.debug(fmt::vformat(description, store)); + logger_.debug(fmt::vformat(details, store)); + } +private: + std::unordered_map default_values_; + logger::logger logger_{ "snitch" }; }; template diff --git a/libs/snitch/inc/public/tfc/snitch/format_extension.hpp b/libs/snitch/inc/public/tfc/snitch/format_extension.hpp new file mode 100644 index 0000000000..8bccc7f297 --- /dev/null +++ b/libs/snitch/inc/public/tfc/snitch/format_extension.hpp @@ -0,0 +1,129 @@ +#pragma once + +#include +#include + +#include + +namespace tfc::snitch::detail { + +template +struct custom_handler { + constexpr void on_text(const Char*, const Char*) {} + + constexpr auto on_arg_id() -> int { + num_args++; + all_named = false; + return 0; + } + + constexpr auto on_arg_id([[maybe_unused]] int id) -> int { + num_args++; + all_named = false; + return 0; + } + + constexpr auto on_arg_id(fmt::basic_string_view id) -> int { + num_args++; + return 0; + } + + constexpr void on_replacement_field(int id, const Char* begin) { + // todo + } + + constexpr auto on_format_specs(int id, const Char* begin, const Char*) -> const Char* { + has_format_specs = true; + return begin; + } + + constexpr void on_error(const char* msg) { + // todo + } + std::size_t num_args{ 0 }; + bool all_named{ true }; + bool has_format_specs{ false }; +}; + +template +consteval auto check_all_arguments_named(std::basic_string_view format_str) -> bool { + custom_handler handler; + fmt::detail::parse_format_string(fmt::basic_string_view{ format_str.data(), format_str.size() }, handler); + return handler.all_named; +} + +template +consteval auto check_all_arguments_no_format(std::basic_string_view format_str) -> bool { + custom_handler handler; + fmt::detail::parse_format_string(fmt::basic_string_view{ format_str.data(), format_str.size() }, handler); + return !handler.has_format_specs; +} + +template +consteval auto arg_count(std::basic_string_view format_str) -> std::size_t { + custom_handler handler; + fmt::detail::parse_format_string(fmt::basic_string_view{ format_str.data(), format_str.size() }, handler); + return handler.num_args; +} + + +template +struct custom_handler_names { + constexpr void on_text(const Char*, const Char*) {} + + constexpr auto on_arg_id() -> int { + return 0; + } + + constexpr auto on_arg_id([[maybe_unused]] int id) -> int { + return 0; + } + + constexpr auto on_arg_id(fmt::basic_string_view id) -> int { + if (auto it{ std::ranges::find_if(names, [id](const auto& name) { return name == id; }) }; it == names.end()) { + names[idx++] = { id.data(), id.size() }; + } + return 0; + } + + constexpr void on_replacement_field(int id, const Char* begin) { + // todo + } + + constexpr auto on_format_specs(int id, const Char* begin, const Char*) -> const Char* { + return begin; + } + + constexpr void on_error(const char* msg) { + // todo + } + std::array, N> names{}; + std::size_t idx{}; +}; + +template +struct arg_names_impl { + static constexpr auto unique_names() noexcept { + using Char = char; + custom_handler_names handler; + fmt::detail::parse_format_string(fmt::basic_string_view{ format_str.data(), format_str.size() }, handler); + return handler.idx; + } + static constexpr auto impl() noexcept { + using Char = char; + custom_handler_names handler; + fmt::detail::parse_format_string(fmt::basic_string_view{ format_str.data(), format_str.size() }, handler); + return handler.names; + } + static constexpr auto num_args = arg_count(format_str); + static constexpr auto arr = impl(); + static constexpr auto size = unique_names(); + static constexpr std::span value{ arr.data(), size }; +}; + +template +consteval auto arg_names() -> decltype(auto) { + return arg_names_impl::value; +} + +} // namespace tfc::snitch::detail diff --git a/libs/snitch/tests/snitch_test.cpp b/libs/snitch/tests/snitch_test.cpp index 9bb3387cd5..372c5577ff 100644 --- a/libs/snitch/tests/snitch_test.cpp +++ b/libs/snitch/tests/snitch_test.cpp @@ -3,9 +3,40 @@ #include #include +#include using std::string_view_literals::operator""sv; +namespace test { +using tfc::snitch::detail::check_all_arguments_named; +static_assert(check_all_arguments_named(glz::chars<"foo">)); +static_assert(check_all_arguments_named(glz::chars<"foo {name}">)); +static_assert(!check_all_arguments_named(glz::chars<"foo {}">)); +static_assert(!check_all_arguments_named(glz::chars<"foo {:.2f}">)); + +using tfc::snitch::detail::check_all_arguments_no_format; +static_assert(check_all_arguments_no_format(glz::chars<"foo {name}">)); +static_assert(!check_all_arguments_no_format(glz::chars<"foo {name:.2f}">)); + +using tfc::snitch::detail::arg_count; +static_assert(arg_count(glz::chars<"foo">) == 0); +static_assert(arg_count(glz::chars<"foo {name}">) == 1); +static_assert(arg_count(glz::chars<"foo {name} {name}">) == 2); +static_assert(arg_count(glz::chars<"foo {name} {name} {name}">) == 3); + +using tfc::snitch::detail::arg_names; +static_assert(arg_names>()[0] == "name"); +static_assert(arg_names>()[0] == "name"); + +static_assert(arg_names>().size() == 1); +// todo this below shouldn't be possible, remove if causes compile error and celebrate +static_assert(arg_names>()[1] == ""); + +static_assert(arg_names>()[1] == "bar"); +static_assert(arg_names>().size() == 2); + +} // namespace test + auto main(int argc, char** argv) -> int { using boost::ut::operator""_test; using boost::ut::expect; @@ -13,9 +44,10 @@ auto main(int argc, char** argv) -> int { tfc::base::init(argc, argv); "snitches"_test = [] { - tfc::snitch::info<"short desc", "long desc"> info(fmt::arg("key1", "value"), fmt::arg("key2", 1)); - fmt::print("{}", 1); - // warn.on_acknowledgement([]{}); + tfc::snitch::info<"short desc {name}", "long desc {name} {index}"> tank(fmt::arg("name", "hello"), fmt::arg("index", 42)); + tank.set(); + + // warn.on_ack([]{}); }; } From 534d7fb6f68c3ad7194216db50df8050b62ac0f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Tue, 18 Jun 2024 15:14:17 +0000 Subject: [PATCH 04/42] add api types for listalarms and listactivations --- libs/snitch/CMakeLists.txt | 1 + libs/snitch/inc/public/tfc/snitch.hpp | 1 + libs/snitch/inc/public/tfc/snitch/common.hpp | 27 +++++++++++++++++++ .../public/tfc/snitch/details/dbus_client.hpp | 18 +++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp diff --git a/libs/snitch/CMakeLists.txt b/libs/snitch/CMakeLists.txt index 9d5c9bf958..7e675d023d 100644 --- a/libs/snitch/CMakeLists.txt +++ b/libs/snitch/CMakeLists.txt @@ -21,6 +21,7 @@ target_link_libraries(snitch Boost::boost tfc::stx tfc::logger + tfc::dbus_util PRIVATE tfc::base ) diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index a80047e3c4..e9116535c0 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace tfc::snitch { diff --git a/libs/snitch/inc/public/tfc/snitch/common.hpp b/libs/snitch/inc/public/tfc/snitch/common.hpp index 4536ba3ba3..293d2aa524 100644 --- a/libs/snitch/inc/public/tfc/snitch/common.hpp +++ b/libs/snitch/inc/public/tfc/snitch/common.hpp @@ -11,4 +11,31 @@ enum struct level : std::uint8_t { error, }; +namespace api { + +struct alarm { + std::string description; + std::string details; + bool active{}; + level lvl{ level::unknown }; + bool latching{}; + struct translation { + std::string description; + std::string details; + }; + using locale = std::string; + std::unordered_map translations; +}; + +struct activation { + std::string description; + std::string details; + bool active{}; + level lvl{ level::unknown }; + bool latching{}; + std::chrono::time_point timestamp; +}; + +} // namespace api + } // namespace tfc::snitch diff --git a/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp new file mode 100644 index 0000000000..69400280ed --- /dev/null +++ b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +namespace tfc::snitch::detail { + +class dbus_client { +public: + dbus_client(std::shared_ptr conn) : dbus_{ std::move(conn) } {} + + void register_alarm(std::string_view alarm_name, level lvl, bool ackable); + +private: + std::shared_ptr dbus_; +}; + +} // namespace tfc::snitch::detail From f622d879728a5a3cc36a4ca18b46d85ac22535aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Tue, 18 Jun 2024 15:29:02 +0000 Subject: [PATCH 05/42] list of api methods in header --- libs/snitch/inc/public/tfc/snitch.hpp | 10 +++++----- libs/snitch/inc/public/tfc/snitch/common.hpp | 17 +++++++++++++---- .../public/tfc/snitch/details/dbus_client.hpp | 18 ++++++++++++++++-- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index e9116535c0..73d3299f98 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -17,7 +17,7 @@ namespace tfc::snitch { struct variance { bool requires_acknowledgement{}; - level lvl{ level::unknown }; + level_e lvl{ level_e::unknown }; }; template @@ -63,15 +63,15 @@ class alarm { }; template -using info = alarm<{ .requires_acknowledgement = false, .lvl = level::info }, description, details>; +using info = alarm<{ .requires_acknowledgement = false, .lvl = level_e::info }, description, details>; template -using warning = alarm<{ .requires_acknowledgement = false, .lvl = level::warning }, description, details>; +using warning = alarm<{ .requires_acknowledgement = false, .lvl = level_e::warning }, description, details>; template -using warning_latched = alarm<{ .requires_acknowledgement = true, .lvl = level::warning }, description, details>; +using warning_latched = alarm<{ .requires_acknowledgement = true, .lvl = level_e::warning }, description, details>; template using warning_ack = warning_latched; template -using error = alarm<{ .requires_acknowledgement = false, .lvl = level::error }, description, details>; +using error = alarm<{ .requires_acknowledgement = false, .lvl = level_e::error }, description, details>; } // namespace tfc::snitch diff --git a/libs/snitch/inc/public/tfc/snitch/common.hpp b/libs/snitch/inc/public/tfc/snitch/common.hpp index 293d2aa524..1bb86b98d6 100644 --- a/libs/snitch/inc/public/tfc/snitch/common.hpp +++ b/libs/snitch/inc/public/tfc/snitch/common.hpp @@ -4,7 +4,7 @@ namespace tfc::snitch { -enum struct level : std::uint8_t { +enum struct level_e : std::uint8_t { unknown = 0, info, warning, @@ -13,11 +13,20 @@ enum struct level : std::uint8_t { namespace api { +using time_point = std::chrono::time_point; +using alarm_id_t = std::uint32_t; + +enum struct active_e : std::int32_t { + all = -1, + inactive = 0, + active = 1 +}; + struct alarm { std::string description; std::string details; bool active{}; - level lvl{ level::unknown }; + level_e lvl{ level_e::unknown }; bool latching{}; struct translation { std::string description; @@ -31,9 +40,9 @@ struct activation { std::string description; std::string details; bool active{}; - level lvl{ level::unknown }; + level_e lvl{ level_e::unknown }; bool latching{}; - std::chrono::time_point timestamp; + time_point timestamp; }; } // namespace api diff --git a/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp index 69400280ed..09ab5e60f1 100644 --- a/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp +++ b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp @@ -1,5 +1,8 @@ #pragma once +#include +#include + #include #include @@ -9,8 +12,19 @@ class dbus_client { public: dbus_client(std::shared_ptr conn) : dbus_{ std::move(conn) } {} - void register_alarm(std::string_view alarm_name, level lvl, bool ackable); - + auto register_alarm(std::string_view tfc_id, std::string_view description, std::string_view details, level_e lvl, bool ackable) -> api::alarm_id_t; + + auto list_alarms() -> std::vector; + + auto list_activations(std::string_view locale, std::uint64_t start_count, std::uint64_t count, level_e, api::active_e, api::time_point start, api::time_point end) -> std::vector; + + auto set_alarm(api::alarm_id_t, std::unordered_map args) -> void; + + auto reset_alarm(api::alarm_id_t) -> void; + + auto ack_alarm(api::alarm_id_t) -> void; + + auto ack_all_alarms() -> void; private: std::shared_ptr dbus_; }; From 721e8b1206b64cae0c53ff011165a4729953d589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Tue, 18 Jun 2024 15:30:12 +0000 Subject: [PATCH 06/42] using 64 bit int for alarm id --- libs/snitch/inc/public/tfc/snitch/common.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/snitch/inc/public/tfc/snitch/common.hpp b/libs/snitch/inc/public/tfc/snitch/common.hpp index 1bb86b98d6..00dd069510 100644 --- a/libs/snitch/inc/public/tfc/snitch/common.hpp +++ b/libs/snitch/inc/public/tfc/snitch/common.hpp @@ -14,7 +14,7 @@ enum struct level_e : std::uint8_t { namespace api { using time_point = std::chrono::time_point; -using alarm_id_t = std::uint32_t; +using alarm_id_t = std::int64_t; enum struct active_e : std::int32_t { all = -1, From 41562e83d187212ba322143a0107963a2e317602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Tue, 18 Jun 2024 15:34:38 +0000 Subject: [PATCH 07/42] add sdbusplus to cmake target --- libs/snitch/CMakeLists.txt | 3 +++ libs/snitch/src/dbus_client.cpp | 4 ++++ libs/snitch/src/snitch.cpp | 2 -- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 libs/snitch/src/dbus_client.cpp delete mode 100644 libs/snitch/src/snitch.cpp diff --git a/libs/snitch/CMakeLists.txt b/libs/snitch/CMakeLists.txt index 7e675d023d..1f91e20066 100644 --- a/libs/snitch/CMakeLists.txt +++ b/libs/snitch/CMakeLists.txt @@ -14,6 +14,8 @@ target_include_directories(snitch find_package(Boost REQUIRED COMPONENTS ) find_package(fmt CONFIG REQUIRED) +find_package(PkgConfig REQUIRED) +pkg_check_modules(SDBUSPLUS REQUIRED IMPORTED_TARGET GLOBAL sdbusplus) target_link_libraries(snitch PUBLIC @@ -24,6 +26,7 @@ target_link_libraries(snitch tfc::dbus_util PRIVATE tfc::base + PkgConfig::SDBUSPLUS ) add_library_to_docs(tfc::snitch) diff --git a/libs/snitch/src/dbus_client.cpp b/libs/snitch/src/dbus_client.cpp new file mode 100644 index 0000000000..ebdf5f83a1 --- /dev/null +++ b/libs/snitch/src/dbus_client.cpp @@ -0,0 +1,4 @@ + +#include + +#include diff --git a/libs/snitch/src/snitch.cpp b/libs/snitch/src/snitch.cpp deleted file mode 100644 index bfaf531b1f..0000000000 --- a/libs/snitch/src/snitch.cpp +++ /dev/null @@ -1,2 +0,0 @@ - -#include From 73e0e9578c2955bf3f258bb8c64580870285657c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Tue, 18 Jun 2024 15:35:59 +0000 Subject: [PATCH 08/42] renamed file --- libs/snitch/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/snitch/CMakeLists.txt b/libs/snitch/CMakeLists.txt index 1f91e20066..7e377af99d 100644 --- a/libs/snitch/CMakeLists.txt +++ b/libs/snitch/CMakeLists.txt @@ -1,7 +1,7 @@ project(snitch) add_library(snitch - src/snitch.cpp + src/dbus_client.cpp ) add_library(tfc::snitch ALIAS snitch) target_include_directories(snitch From 57f1df0f364b170d47320bbf91eb7acd8770e7ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Tue, 18 Jun 2024 15:39:11 +0000 Subject: [PATCH 09/42] Themis impl (#589) * Added structure for alarm system server * Remove snitch.md from diff * Update exes/themis/inc/alarm_database.hpp --- exes/CMakeLists.txt | 3 +- exes/themis/CMakeLists.txt | 54 ++++++++++++++ exes/themis/inc/alarm_database.hpp | 82 ++++++++++++++++++++++ exes/themis/src/main.cpp | 14 ++++ exes/themis/tests/CMakeLists.txt | 19 +++++ exes/themis/tests/themis_database_test.cpp | 18 +++++ 6 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 exes/themis/CMakeLists.txt create mode 100644 exes/themis/inc/alarm_database.hpp create mode 100644 exes/themis/src/main.cpp create mode 100644 exes/themis/tests/CMakeLists.txt create mode 100644 exes/themis/tests/themis_database_test.cpp diff --git a/exes/CMakeLists.txt b/exes/CMakeLists.txt index f5c6106105..91d12de1f2 100644 --- a/exes/CMakeLists.txt +++ b/exes/CMakeLists.txt @@ -5,4 +5,5 @@ add_subdirectory(operations) add_subdirectory(tfcctl) add_subdirectory(ipc-ruler) add_subdirectory(signal_source) -add_subdirectory(mqtt-bridge) \ No newline at end of file +add_subdirectory(mqtt-bridge) +add_subdirectory(themis) \ No newline at end of file diff --git a/exes/themis/CMakeLists.txt b/exes/themis/CMakeLists.txt new file mode 100644 index 0000000000..19fb438a7e --- /dev/null +++ b/exes/themis/CMakeLists.txt @@ -0,0 +1,54 @@ +project(themis) +add_executable(themis src/main.cpp) + +find_package(Boost REQUIRED COMPONENTS program_options) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(SDBUSPLUS REQUIRED IMPORTED_TARGET GLOBAL sdbusplus) + +find_package(unofficial-sqlite3 CONFIG REQUIRED) +find_path(SQLITE_MODERN_CPP_INCLUDE_DIRS "sqlite_modern_cpp.h") +target_include_directories(themis + PRIVATE + $ + PUBLIC + inc +) + +target_link_libraries(themis + PUBLIC + tfc::ipc + tfc::base + tfc::confman + tfc::logger + tfc::dbus_util + Boost::program_options + PkgConfig::SDBUSPLUS + unofficial::sqlite3::sqlite3 +) + +add_subdirectory(tests) + +include(tfc_split_debug_info) +tfc_split_debug_info(themis) + +include(GNUInstallDirs) + +install( + TARGETS + themis + DESTINATION + ${CMAKE_INSTALL_BINDIR} + CONFIGURATIONS Release +) + +install( + TARGETS + themis + DESTINATION + ${CMAKE_INSTALL_BINDIR}/debug/ + CONFIGURATIONS Debug +) + +include(tfc_systemd) +tfc_systemd_service_file(themis "TFC themis - alarm system") diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp new file mode 100644 index 0000000000..f8c885173b --- /dev/null +++ b/exes/themis/inc/alarm_database.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace tfc::themis { +inline auto config_file_name_populate_dir() -> std::string { + auto const file{ base::make_config_file_name(base::get_exe_name(), "db") }; + std::filesystem::create_directories(file.parent_path()); + return file.string(); +} +class alarm_database { +public: + explicit alarm_database(bool in_memory = false) : db_(in_memory ? ":memory:" : config_file_name_populate_dir()) { + db_ << R"( +CREATE TABLE IF NOT EXISTS Alarms( + alarm_id INTEGER PRIMARY KEY, + tfc_id TEXT NOT NULL, + sha1sum TEXT NOT NULL, + alarm_level INTEGER NOT NULL, + short_msg_en TEXT NOT NULL, + msg_en TEXT NOT NULL, + alarm_latching BOOLEAN NOT NULL, + UNIQUE(tfc_id, sha1sum) ON CONFLICT IGNORE +); + )"; + db_ << R"( +CREATE TABLE IF NOT EXISTS AlarmTranslation( + sha1sum TEXT PRIMARY KEY, + locale TEXT NOT NULL, + short_msg TEXT NOT NULL, + msg TEXT NOT NULL, + FOREIGN KEY(sha1sum) REFERENCES Alarms(sha1sum) +); +)"; + // Timestamp stored as long integer to get millisecond precision + // And because sqlite does not offer a native timestamp type. + db_ << R"( +CREATE TABLE IF NOT EXISTS AlarmActivations( + activation_id INTEGER PRIMARY KEY, + alarm_id INTEGER NOT NULL, + activation_time LONG INTEGER NOT NULL, + activation_level INTEGER NOT NULL, + FOREIGN KEY(alarm_id) REFERENCES Alarms(alarm_id) +); +)"; + db_ << R"( +CREATE TABLE IF NOT EXISTS AlarmAcks( + activation_id INTEGER, + ack_timestamp LONG INTEGER NOT NULL, + FOREIGN KEY(activation_id) REFERENCES AlarmActivations(activation_id) +); +)"; + db_ << R"( +CREATE TABLE IF NOT EXISTS AlarmVariables( + activation_id INTEGER, + variable_key TEXT NOT NULL, + variable_value TEXT NOT NULL, + FOREIGN KEY(activation_id) REFERENCES AlarmActivations(activation_id) +); +)"; + } + /** + * @brief Register an alarm in the database + * @param tfc_id The TFC ID of the alarm + * @param msg The message of the alarm + * @param short_msg The short message of the alarm + * @param latching Whether the alarm is latching + * @param alarm_level The alarm level + * @return The alarm ID + */ + [[nodiscard]] auto register_alarm(std::string_view tfc_id, std::string_view msg, std::string_view short_msg, bool latching, std::uint32_t alarm_level) -> std::int64_t { + db_ << fmt::format("INSERT INTO Alarms(tfc_id, sha1sum, alarm_level, short_msg_en, msg_en, alarm_latching) VALUES('{}','{}',{},'{}','{}',{});", tfc_id, msg, alarm_level, short_msg, msg, latching ? 1 : 0); + return db_.last_insert_rowid(); + } +private: + sqlite::database db_; +}; +} diff --git a/exes/themis/src/main.cpp b/exes/themis/src/main.cpp new file mode 100644 index 0000000000..2737b5f0e4 --- /dev/null +++ b/exes/themis/src/main.cpp @@ -0,0 +1,14 @@ +#include +#include +#include +#include + +namespace po = boost::program_options; + +auto main(int argc, char** argv) -> int { + auto description{ tfc::base::default_description() }; + bool in_memory = false; + description.add_options()("memory", po::bool_switch(&in_memory), "Run the database in memory, non persistent"); + tfc::base::init(argc, argv, description); + return 0; +} diff --git a/exes/themis/tests/CMakeLists.txt b/exes/themis/tests/CMakeLists.txt new file mode 100644 index 0000000000..c7e03bac7e --- /dev/null +++ b/exes/themis/tests/CMakeLists.txt @@ -0,0 +1,19 @@ +find_package(ut CONFIG REQUIRED) +find_package(GTest CONFIG REQUIRED COMPONENTS gmock) +find_path(BEXT_SML_INCLUDE_DIRS "boost/sml.hpp") + +add_executable(themis_database_test themis_database_test.cpp) +find_package(unofficial-sqlite3 CONFIG REQUIRED) +find_path(SQLITE_MODERN_CPP_INCLUDE_DIRS "sqlite_modern_cpp.h") + +target_link_libraries(themis_database_test + PRIVATE + Boost::ut + tfc::base + unofficial::sqlite3::sqlite3 +) + +target_include_directories(themis_database_test + PRIVATE + ../inc +) \ No newline at end of file diff --git a/exes/themis/tests/themis_database_test.cpp b/exes/themis/tests/themis_database_test.cpp new file mode 100644 index 0000000000..993d145473 --- /dev/null +++ b/exes/themis/tests/themis_database_test.cpp @@ -0,0 +1,18 @@ +#include +#include +#include +#include + +namespace ut = boost::ut; + +using boost::ut::operator""_test; + +auto main(int argc, char** argv) -> int { + tfc::base::init(argc, argv); + + //"Database correctness check"_test = [] { + tfc::themis::alarm_database alarm_db(false); + auto insert_id = alarm_db.register_alarm("tfc_id", "msg", "short msg", false, 0); + std::cout << insert_id << std::endl; + //}; +} From 53fbcc4e7b92a1e612b52208573d3e48dd88c4c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Tue, 18 Jun 2024 15:51:36 +0000 Subject: [PATCH 10/42] add class functions in source file --- .../public/tfc/snitch/details/dbus_client.hpp | 5 ++- libs/snitch/src/dbus_client.cpp | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp index 09ab5e60f1..98a53e7ba4 100644 --- a/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp +++ b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp @@ -10,8 +10,9 @@ namespace tfc::snitch::detail { class dbus_client { public: - dbus_client(std::shared_ptr conn) : dbus_{ std::move(conn) } {} + explicit dbus_client(std::shared_ptr conn); + // todo completion tokens ... std function auto register_alarm(std::string_view tfc_id, std::string_view description, std::string_view details, level_e lvl, bool ackable) -> api::alarm_id_t; auto list_alarms() -> std::vector; @@ -25,6 +26,8 @@ class dbus_client { auto ack_alarm(api::alarm_id_t) -> void; auto ack_all_alarms() -> void; + + auto match_ack_alarm(api::alarm_id_t, std::function callback) -> void; private: std::shared_ptr dbus_; }; diff --git a/libs/snitch/src/dbus_client.cpp b/libs/snitch/src/dbus_client.cpp index ebdf5f83a1..d5df93d2dd 100644 --- a/libs/snitch/src/dbus_client.cpp +++ b/libs/snitch/src/dbus_client.cpp @@ -2,3 +2,34 @@ #include #include + +namespace tfc::snitch::detail { + +dbus_client::dbus_client(std::shared_ptr conn) : dbus_{ std::move(conn) } { +} + +auto dbus_client::register_alarm(std::string_view tfc_id, std::string_view description, std::string_view details, level_e lvl, bool ackable) -> api::alarm_id_t { + return 0; +} + +auto dbus_client::list_alarms() -> std::vector { + return {}; +} + +auto dbus_client::list_activations(std::string_view locale, std::uint64_t start_count, std::uint64_t count, level_e, api::active_e, api::time_point start, api::time_point end) -> std::vector { + return {}; +} + +auto dbus_client::set_alarm(api::alarm_id_t, std::unordered_map args) -> void { +} + +auto dbus_client::reset_alarm(api::alarm_id_t) -> void { +} + +auto dbus_client::ack_alarm(api::alarm_id_t) -> void { +} + +auto dbus_client::ack_all_alarms() -> void { +} + +} // namespace tfc::snitch::detail From 4faefa323c7e26ca017c987effb864a6524fceda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Tue, 18 Jun 2024 17:19:24 +0000 Subject: [PATCH 11/42] Added register alarm, fetch alarms and register translation --- exes/themis/inc/alarm_database.hpp | 104 +++++++++++++++++-- exes/themis/tests/CMakeLists.txt | 3 + exes/themis/tests/themis_database_test.cpp | 60 +++++++++-- libs/snitch/inc/public/tfc/snitch/common.hpp | 6 +- 4 files changed, 158 insertions(+), 15 deletions(-) diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index f8c885173b..e121002900 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -1,12 +1,19 @@ #pragma once +#include +#include +#include +#include #include #include #include -#include -#include +#include namespace tfc::themis { + +using enum tfc::snitch::level_e; +using enum tfc::snitch::api::active_e; + inline auto config_file_name_populate_dir() -> std::string { auto const file{ base::make_config_file_name(base::get_exe_name(), "db") }; std::filesystem::create_directories(file.parent_path()); @@ -29,10 +36,11 @@ CREATE TABLE IF NOT EXISTS Alarms( )"; db_ << R"( CREATE TABLE IF NOT EXISTS AlarmTranslation( - sha1sum TEXT PRIMARY KEY, + sha1sum TEXT, locale TEXT NOT NULL, short_msg TEXT NOT NULL, msg TEXT NOT NULL, + PRIMARY KEY(sha1sum, locale) ON CONFLICT REPLACE, FOREIGN KEY(sha1sum) REFERENCES Alarms(sha1sum) ); )"; @@ -72,11 +80,93 @@ CREATE TABLE IF NOT EXISTS AlarmVariables( * @param alarm_level The alarm level * @return The alarm ID */ - [[nodiscard]] auto register_alarm(std::string_view tfc_id, std::string_view msg, std::string_view short_msg, bool latching, std::uint32_t alarm_level) -> std::int64_t { - db_ << fmt::format("INSERT INTO Alarms(tfc_id, sha1sum, alarm_level, short_msg_en, msg_en, alarm_latching) VALUES('{}','{}',{},'{}','{}',{});", tfc_id, msg, alarm_level, short_msg, msg, latching ? 1 : 0); - return db_.last_insert_rowid(); + [[nodiscard]] auto register_alarm(std::string_view tfc_id, + std::string_view msg, + std::string_view short_msg, + bool latching, + tfc::snitch::level_e alarm_level) -> std::uint64_t { + std::string sha1_ascii = get_sha1(fmt::format("{}{}", msg, short_msg)); + db_ << fmt::format( + "INSERT INTO Alarms(tfc_id, sha1sum, alarm_level, short_msg_en, msg_en, alarm_latching) " + "VALUES('{}','{}',{},'{}','{}',{});", + tfc_id, sha1_ascii, static_cast(alarm_level), short_msg, msg, latching ? 1 : 0); + std::int64_t last_insert_rowid = db_.last_insert_rowid(); + if (last_insert_rowid < 0) { + throw std::runtime_error("Failed to insert alarm into database"); + } + return static_cast(last_insert_rowid); + } + + [[nodiscard]] auto list_alarms() -> std::vector { + std::unordered_map alarms; + std::string query = R"( +SELECT + alarm_id, + tfc_id, + Alarms.sha1sum, + alarm_level, + Alarms.short_msg_en, + Alarms.msg_en, + alarm_latching, + locale, + AlarmTranslation.short_msg, + AlarmTranslation.msg +FROM Alarms +LEFT JOIN AlarmTranslation +ON Alarms.sha1sum = AlarmTranslation.sha1sum; +)"; + // Accept the second table paramters as unique ptr's as the values can be null, and we want to preserve that + db_ << query >> [&](std::uint64_t alarm_id, std::string tfc_id, std::string sha1sum, std::uint8_t alarm_level, + std::string short_msg_en, std::string msg_en, bool alarm_latching, + std::unique_ptr locale, std::unique_ptr translated_short_msg, + std::unique_ptr translated_msg) { + auto iterator = alarms.find(alarm_id); + if (iterator == alarms.end()) { + alarms.insert({ alarm_id, tfc::snitch::api::alarm{ alarm_id, + tfc_id, + short_msg_en, + msg_en, + sha1sum, + static_cast(alarm_level), + alarm_latching, + {} } }); + } + if (locale != nullptr && translated_short_msg != nullptr && translated_msg != nullptr) { + alarms[alarm_id].translations.insert({ *locale, { *translated_short_msg, *translated_msg } }); + } + }; + std::vector alarm_list; + for (auto& [_, alarm] : alarms) { + alarm_list.push_back(alarm); + } + return alarm_list; + } + + auto add_alarm_translation(std::string_view sha1sum, + std::string_view locale, + std::string_view short_msg, + std::string_view msg) -> void { + db_ << fmt::format("INSERT INTO AlarmTranslation(sha1sum, locale, short_msg, msg) VALUES('{}','{}','{}','{}');", sha1sum, + locale, short_msg, msg); + } + + // Note. Use `echo -n "value" | sha1sum` to not hash the newline character and + // match the results from this function. + [[nodiscard]] static auto get_sha1(std::string_view msg) -> std::string { + // Calculate sha1 + std::array sha1_res; + SHA1(reinterpret_cast(msg.data()), msg.size(), sha1_res.data()); + + // Convert sha1 to ascii + std::string sha1_ascii; + sha1_ascii.reserve(SHA_DIGEST_LENGTH * 2); + for (size_t i = 0; i < SHA_DIGEST_LENGTH; i++) { + sha1_ascii += fmt::format("{:02x}", sha1_res[i]); + } + return sha1_ascii; } + private: sqlite::database db_; }; -} +} // namespace tfc::themis diff --git a/exes/themis/tests/CMakeLists.txt b/exes/themis/tests/CMakeLists.txt index c7e03bac7e..c4371f5685 100644 --- a/exes/themis/tests/CMakeLists.txt +++ b/exes/themis/tests/CMakeLists.txt @@ -4,13 +4,16 @@ find_path(BEXT_SML_INCLUDE_DIRS "boost/sml.hpp") add_executable(themis_database_test themis_database_test.cpp) find_package(unofficial-sqlite3 CONFIG REQUIRED) +find_package(OpenSSL CONFIG REQUIRED) find_path(SQLITE_MODERN_CPP_INCLUDE_DIRS "sqlite_modern_cpp.h") target_link_libraries(themis_database_test PRIVATE Boost::ut tfc::base + tfc::snitch unofficial::sqlite3::sqlite3 + OpenSSL::Crypto ) target_include_directories(themis_database_test diff --git a/exes/themis/tests/themis_database_test.cpp b/exes/themis/tests/themis_database_test.cpp index 993d145473..bc53c48e57 100644 --- a/exes/themis/tests/themis_database_test.cpp +++ b/exes/themis/tests/themis_database_test.cpp @@ -1,18 +1,64 @@ +#include +#include #include #include -#include -#include +#include namespace ut = boost::ut; using boost::ut::operator""_test; +using boost::ut::operator|; +using boost::ut::operator/; +using boost::ut::expect; +using tfc::themis::alarm_database; auto main(int argc, char** argv) -> int { tfc::base::init(argc, argv); - //"Database correctness check"_test = [] { - tfc::themis::alarm_database alarm_db(false); - auto insert_id = alarm_db.register_alarm("tfc_id", "msg", "short msg", false, 0); - std::cout << insert_id << std::endl; - //}; + "sha1sum calculations correctness"_test = + [](auto& test_data) { + std::string input = test_data.first; + std::string expected = test_data.second; + expect(alarm_database::get_sha1(input) == expected) + << fmt::format("{} != {}", alarm_database::get_sha1(input), expected); + } | + std::vector>{ { "msgshort msg", "654e9cca8eb076653201325a5cf07ed24ee71b52" }, + { "this is a test", "fa26be19de6bff93f70bc2308434e4a440bbad02" }, + { "another test", "afc8edc74ae9e7b8d290f945a6d613f1d264a2b2" } }; + + "Database correctness check"_test = [] { + tfc::themis::alarm_database alarm_db(true); + + // Inserted alarm matches fetched alarm + expect(alarm_db.list_alarms().size() == 0); + auto insert_id = alarm_db.register_alarm("tfc_id", "msg", "short msg", false, tfc::snitch::level_e::info); + auto alarms = alarm_db.list_alarms(); + expect(alarms.size() == 1); + expect(alarms.at(0).tfc_id == "tfc_id"); + expect(alarms.at(0).translations.size() == 0); + expect(alarms.at(0).sha1sum == alarm_database::get_sha1("msgshort msg")); + expect(alarms.at(0).lvl == tfc::snitch::level_e::info); + expect(alarms.at(0).alarm_id == insert_id); + + // Translations are attached to alarms + alarm_db.add_alarm_translation(alarm_db.list_alarms()[0].sha1sum, "es", "some spanish maybe", + "this is is also spanish believe me please"); + alarms = alarm_db.list_alarms(); + expect(alarms.size() == 1); + expect(alarms.at(0).translations.size() == 1); + auto iterator = alarms.at(0).translations.find("es"); + expect(iterator != alarms.at(0).translations.end()); + expect(iterator->second.description == "some spanish maybe"); + expect(iterator->second.details == "this is is also spanish believe me please"); + + alarm_db.add_alarm_translation(alarm_db.list_alarms()[0].sha1sum, "is", "Some icelandic really", + "This is really some icelandic"); + alarms = alarm_db.list_alarms(); + expect(alarms.size() == 1); + expect(alarms.at(0).translations.size() == 2); + iterator = alarms.at(0).translations.find("is"); + expect(iterator != alarms.at(0).translations.end()); + expect(iterator->second.description == "Some icelandic really"); + expect(iterator->second.details == "This is really some icelandic"); + }; } diff --git a/libs/snitch/inc/public/tfc/snitch/common.hpp b/libs/snitch/inc/public/tfc/snitch/common.hpp index 00dd069510..8e0d52d036 100644 --- a/libs/snitch/inc/public/tfc/snitch/common.hpp +++ b/libs/snitch/inc/public/tfc/snitch/common.hpp @@ -1,6 +1,8 @@ #pragma once #include +#include +#include namespace tfc::snitch { @@ -23,9 +25,11 @@ enum struct active_e : std::int32_t { }; struct alarm { + std::uint64_t alarm_id; + std::string tfc_id; std::string description; std::string details; - bool active{}; + std::string sha1sum; level_e lvl{ level_e::unknown }; bool latching{}; struct translation { From 5c18d42c4e9665b657b6054b5f18a4a3898cdb07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Tue, 18 Jun 2024 18:22:13 +0000 Subject: [PATCH 12/42] Database design modified all alarm text moved to translation Had some errors with the design. Moved all alarm text to the alarmtranslations table. --- exes/themis/inc/alarm_database.hpp | 235 +++++++++++++++---- exes/themis/tests/themis_database_test.cpp | 49 +++- libs/snitch/inc/public/tfc/snitch/common.hpp | 12 +- 3 files changed, 236 insertions(+), 60 deletions(-) diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index e121002900..e628a787c9 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -3,16 +3,30 @@ #include #include #include +#include #include +#include #include +#include #include +#include #include #include namespace tfc::themis { using enum tfc::snitch::level_e; -using enum tfc::snitch::api::active_e; +using tfc::snitch::api::time_point; + +class error_log { +public: + error_log() : logger_("alarm_database") { + sqlite::error_log([this](const std::exception& e) { logger_.error("{}", e.what()); }); + } + +private: + tfc::logger::logger logger_; +}; inline auto config_file_name_populate_dir() -> std::string { auto const file{ base::make_config_file_name(base::get_exe_name(), "db") }; @@ -22,36 +36,37 @@ inline auto config_file_name_populate_dir() -> std::string { class alarm_database { public: explicit alarm_database(bool in_memory = false) : db_(in_memory ? ":memory:" : config_file_name_populate_dir()) { + // Set foreign key enforcement to modern standards + db_ << "PRAGMA foreign_keys = ON;"; db_ << R"( CREATE TABLE IF NOT EXISTS Alarms( alarm_id INTEGER PRIMARY KEY, tfc_id TEXT NOT NULL, sha1sum TEXT NOT NULL, alarm_level INTEGER NOT NULL, - short_msg_en TEXT NOT NULL, - msg_en TEXT NOT NULL, alarm_latching BOOLEAN NOT NULL, UNIQUE(tfc_id, sha1sum) ON CONFLICT IGNORE ); )"; db_ << R"( -CREATE TABLE IF NOT EXISTS AlarmTranslation( - sha1sum TEXT, +CREATE TABLE IF NOT EXISTS AlarmTranslations( + sha1sum TEXT NOT NULL, + alarm_id INTEGER NOT NULL, locale TEXT NOT NULL, short_msg TEXT NOT NULL, msg TEXT NOT NULL, - PRIMARY KEY(sha1sum, locale) ON CONFLICT REPLACE, - FOREIGN KEY(sha1sum) REFERENCES Alarms(sha1sum) + PRIMARY KEY(sha1sum, locale) ON CONFLICT REPLACE + FOREIGN KEY(alarm_id) REFERENCES Alarms(alarm_id) ); )"; // Timestamp stored as long integer to get millisecond precision // And because sqlite does not offer a native timestamp type. db_ << R"( CREATE TABLE IF NOT EXISTS AlarmActivations( - activation_id INTEGER PRIMARY KEY, + activation_id LONG INTEGER PRIMARY KEY, alarm_id INTEGER NOT NULL, activation_time LONG INTEGER NOT NULL, - activation_level INTEGER NOT NULL, + activation_level BOOLEAN NOT NULL, FOREIGN KEY(alarm_id) REFERENCES Alarms(alarm_id) ); )"; @@ -59,6 +74,7 @@ CREATE TABLE IF NOT EXISTS AlarmActivations( CREATE TABLE IF NOT EXISTS AlarmAcks( activation_id INTEGER, ack_timestamp LONG INTEGER NOT NULL, + UNIQUE(activation_id) ON CONFLICT IGNORE, FOREIGN KEY(activation_id) REFERENCES AlarmActivations(activation_id) ); )"; @@ -80,59 +96,63 @@ CREATE TABLE IF NOT EXISTS AlarmVariables( * @param alarm_level The alarm level * @return The alarm ID */ - [[nodiscard]] auto register_alarm(std::string_view tfc_id, - std::string_view msg, - std::string_view short_msg, - bool latching, - tfc::snitch::level_e alarm_level) -> std::uint64_t { + [[nodiscard]] auto register_alarm_en(std::string_view tfc_id, + std::string_view msg, + std::string_view short_msg, + bool latching, + tfc::snitch::level_e alarm_level) -> std::uint64_t { std::string sha1_ascii = get_sha1(fmt::format("{}{}", msg, short_msg)); - db_ << fmt::format( - "INSERT INTO Alarms(tfc_id, sha1sum, alarm_level, short_msg_en, msg_en, alarm_latching) " - "VALUES('{}','{}',{},'{}','{}',{});", - tfc_id, sha1_ascii, static_cast(alarm_level), short_msg, msg, latching ? 1 : 0); - std::int64_t last_insert_rowid = db_.last_insert_rowid(); - if (last_insert_rowid < 0) { - throw std::runtime_error("Failed to insert alarm into database"); + std::uint64_t alarm_id = 0; + try { + db_ << "BEGIN;"; + db_ << fmt::format( + "INSERT INTO Alarms(tfc_id, sha1sum, alarm_level, alarm_latching) " + "VALUES('{}','{}',{},{});", + tfc_id, sha1_ascii, static_cast(alarm_level), latching ? 1 : 0); + auto insert_id = db_.last_insert_rowid(); + if (insert_id < 0) { + throw std::runtime_error("Failed to insert alarm into database"); + } + alarm_id = static_cast(insert_id); + add_alarm_translation(sha1_ascii, alarm_id, "en", short_msg, msg); + db_ << "COMMIT;"; + } catch (std::exception& e) { + // Rollback the transaction and rethrow + db_ << "ROLLBACK;"; + throw e; } - return static_cast(last_insert_rowid); + return alarm_id; } [[nodiscard]] auto list_alarms() -> std::vector { std::unordered_map alarms; std::string query = R"( SELECT - alarm_id, - tfc_id, + Alarms.alarm_id, + Alarms.tfc_id, Alarms.sha1sum, alarm_level, - Alarms.short_msg_en, - Alarms.msg_en, - alarm_latching, - locale, - AlarmTranslation.short_msg, - AlarmTranslation.msg + Alarms.alarm_latching, + AlarmTranslations.locale, + AlarmTranslations.short_msg, + AlarmTranslations.msg FROM Alarms -LEFT JOIN AlarmTranslation -ON Alarms.sha1sum = AlarmTranslation.sha1sum; +LEFT JOIN AlarmTranslations +ON Alarms.sha1sum = AlarmTranslations.sha1sum; )"; // Accept the second table paramters as unique ptr's as the values can be null, and we want to preserve that db_ << query >> [&](std::uint64_t alarm_id, std::string tfc_id, std::string sha1sum, std::uint8_t alarm_level, - std::string short_msg_en, std::string msg_en, bool alarm_latching, - std::unique_ptr locale, std::unique_ptr translated_short_msg, - std::unique_ptr translated_msg) { + bool alarm_latching, std::optional locale, + std::optional translated_short_msg, std::optional translated_msg) { auto iterator = alarms.find(alarm_id); if (iterator == alarms.end()) { - alarms.insert({ alarm_id, tfc::snitch::api::alarm{ alarm_id, - tfc_id, - short_msg_en, - msg_en, - sha1sum, - static_cast(alarm_level), - alarm_latching, - {} } }); + alarms.insert( + { alarm_id, + tfc::snitch::api::alarm{ + alarm_id, tfc_id, sha1sum, static_cast(alarm_level), alarm_latching, {} } }); } - if (locale != nullptr && translated_short_msg != nullptr && translated_msg != nullptr) { - alarms[alarm_id].translations.insert({ *locale, { *translated_short_msg, *translated_msg } }); + if (locale.has_value() && translated_short_msg.has_value() && translated_msg.has_value()) { + alarms[alarm_id].translations.insert({ locale.value(), { translated_short_msg.value(), translated_msg.value() } }); } }; std::vector alarm_list; @@ -143,11 +163,112 @@ ON Alarms.sha1sum = AlarmTranslation.sha1sum; } auto add_alarm_translation(std::string_view sha1sum, + std::uint64_t alarm_id, std::string_view locale, std::string_view short_msg, std::string_view msg) -> void { - db_ << fmt::format("INSERT INTO AlarmTranslation(sha1sum, locale, short_msg, msg) VALUES('{}','{}','{}','{}');", sha1sum, - locale, short_msg, msg); + auto query = fmt::format( + "INSERT INTO AlarmTranslations(sha1sum, alarm_id, locale, short_msg, msg) VALUES('{}', {}, '{}','{}','{}');", + sha1sum, alarm_id, locale, short_msg, msg); + std::cerr << query << std::endl; + db_ << query; + } + + auto set_alarm(std::uint64_t alarm_id, + std::unordered_map variables, + std::optional tp = {}) -> void { + db_ << fmt::format("INSERT INTO AlarmActivations(alarm_id, activation_time, activation_level) VALUES({},{},1)", alarm_id, + milliseconds_since_epoch(tp)); + std::int64_t last_insert_rowid = db_.last_insert_rowid(); + if (last_insert_rowid < 0) { + throw std::runtime_error("Failed to insert activation into database"); + } + + for (auto& [key, value] : variables) { + db_ << fmt::format("INSERT INTO AlarmVariables(activation_id, variable_key, variable_value) VALUES({},{},'{}');", + last_insert_rowid, key, value); + } + } + auto reset_alarm(std::uint64_t alarm_id, std::optional tp = {}) -> void { + db_ << fmt::format("INSERT INTO AlarmActivations(alarm_id, activation_time, activation_level) VALUES({},{},0)", alarm_id, + milliseconds_since_epoch(tp)); + } + + auto ack_alarm(std::uint64_t activation_id, std::optional tp = {}) -> void { + db_ << fmt::format("INSERT INTO AlarmAcks(activation_id, ack_timestamp) VALUES({},{});", activation_id, + milliseconds_since_epoch(tp)); + } + + auto ack_all_alarms(std::optional tp = {}) -> void { + // TODO: This is not 100% but we should be able to do this in a single query. Very fast. + // Gonna build up some more elaborate test cases and dig into this when that is complete + db_ << fmt::format( + "INSERT INTO AlarmAcks(activation_id, ack_timestamp) SELECT DISTINCT activation_id, {} FROM AlarmActivations;", + milliseconds_since_epoch(tp)); + } + + [[nodiscard]] auto list_activations(std::string_view locale, + std::uint64_t start_count, + std::uint64_t count, + tfc::snitch::level_e level, + tfc::snitch::api::active_e active, + std::optional start, + std::optional end) + -> std::vector { + std::string populated_query = fmt::format(R"(SELECT + activation_id, + Alarms.alarm_id, + activation_time, + activation_level, + AlarmTranslations.short_msg, + AlarmTranslations.msg, + AlarmTranslations.locale, + Alarms.alarm_latching, + Alarms.alarm_level +FROM AlarmActivations +JOIN Alarms on (Alarms.alarm_id = AlarmActivations.alarm_id) +LEFT OUTER JOIN AlarmTranslations on (Alarms.sha1sum = AlarmTranslations.sha1sum) +WHERE (AlarmTranslations.locale = '{}' OR AlarmTranslations.locale = 'en') AND activation_time >= {} AND activation_time <= {})", + locale, milliseconds_since_epoch(start), milliseconds_since_epoch(end)); + if (level != tfc::snitch::level_e::unknown) { + populated_query += fmt::format(" AND alarm_level = {}", static_cast(level)); + } + if (active != tfc::snitch::api::active_e::all) { + populated_query += fmt::format(" AND activation_level = {}", static_cast(active)); + } + populated_query += fmt::format(" LIMIT {} OFFSET {};", count, start_count); + + std::unordered_map activations; + + db_ << populated_query >> [&](std::uint64_t activation_id, std::uint64_t alarm_id, std::int64_t activation_time, + bool activation_level, std::optional translated_short_msg, + std::optional translated_msg, std::optional tlocale, + bool alarm_latching, std::uint8_t alarm_level) { + // This callback can be called multiple times for the same activation_id + // Once with the locale requested and once with the english locale + // I am going to try and solve that with a group by clause + + // For now we do this in c++, if we have value and the underlying element is not found or there is no element and the locale is wrong + if (translated_msg.has_value() && translated_short_msg.has_value() && tlocale.has_value()) { + auto iterator = activations.find(activation_id); + if (iterator != activations.end() || iterator->second.locale == locale) { + activations[activation_id] = { alarm_id, + activation_id, + translated_msg.value(), + translated_short_msg.value(), + tlocale.value(), + activation_level, + static_cast(alarm_level), + alarm_latching, + timepoint_from_milliseconds(activation_time) }; + } + } + }; + std::vector ret_value; + for (auto& [_, activation] : activations) { + ret_value.push_back(activation); + } + return ret_value; } // Note. Use `echo -n "value" | sha1sum` to not hash the newline character and @@ -160,13 +281,31 @@ ON Alarms.sha1sum = AlarmTranslation.sha1sum; // Convert sha1 to ascii std::string sha1_ascii; sha1_ascii.reserve(SHA_DIGEST_LENGTH * 2); - for (size_t i = 0; i < SHA_DIGEST_LENGTH; i++) { + for (std::size_t i = 0; i < SHA_DIGEST_LENGTH; i++) { sha1_ascii += fmt::format("{:02x}", sha1_res[i]); } return sha1_ascii; } + static constexpr tfc::snitch::api::time_point timepoint_from_milliseconds(std::int64_t ms) { + return tfc::snitch::api::time_point(std::chrono::milliseconds(ms)); + } + private: + static std::int64_t milliseconds_since_epoch(std::optional tp) { + tfc::snitch::api::time_point value = + tp.value_or(std::chrono::time_point_cast(std::chrono::system_clock::now())); + return milliseconds_since_epoch_base(value); + } + + static constexpr std::int64_t milliseconds_since_epoch_base(tfc::snitch::api::time_point tp) { + return std::chrono::duration_cast(tp.time_since_epoch()).count(); + } + + // TODO: Want this to work + // static_assert(milliseconds_since_epoch_base(timepoint_from_milliseconds(1000)) == 1000); + + error_log log_{}; sqlite::database db_; }; } // namespace tfc::themis diff --git a/exes/themis/tests/themis_database_test.cpp b/exes/themis/tests/themis_database_test.cpp index bc53c48e57..8effbfc37b 100644 --- a/exes/themis/tests/themis_database_test.cpp +++ b/exes/themis/tests/themis_database_test.cpp @@ -10,6 +10,7 @@ using boost::ut::operator""_test; using boost::ut::operator|; using boost::ut::operator/; using boost::ut::expect; +using boost::ut::throws; using tfc::themis::alarm_database; auto main(int argc, char** argv) -> int { @@ -31,34 +32,68 @@ auto main(int argc, char** argv) -> int { // Inserted alarm matches fetched alarm expect(alarm_db.list_alarms().size() == 0); - auto insert_id = alarm_db.register_alarm("tfc_id", "msg", "short msg", false, tfc::snitch::level_e::info); + auto insert_id = alarm_db.register_alarm_en("tfc_id", "msg", "short msg", false, tfc::snitch::level_e::info); auto alarms = alarm_db.list_alarms(); expect(alarms.size() == 1); expect(alarms.at(0).tfc_id == "tfc_id"); - expect(alarms.at(0).translations.size() == 0); + expect(alarms.at(0).translations.size() == 1); // English is always present expect(alarms.at(0).sha1sum == alarm_database::get_sha1("msgshort msg")); expect(alarms.at(0).lvl == tfc::snitch::level_e::info); expect(alarms.at(0).alarm_id == insert_id); + auto iterator = alarms.at(0).translations.find("en"); + expect(iterator != alarms.at(0).translations.end()); + expect(iterator->second.details == "msg") << iterator->second.description; + expect(iterator->second.description == "short msg") << iterator->second.details; // Translations are attached to alarms - alarm_db.add_alarm_translation(alarm_db.list_alarms()[0].sha1sum, "es", "some spanish maybe", + alarm_db.add_alarm_translation(alarm_db.list_alarms().at(0).sha1sum, alarm_db.list_alarms().at(0).alarm_id, "es", "some spanish maybe", "this is is also spanish believe me please"); alarms = alarm_db.list_alarms(); expect(alarms.size() == 1); - expect(alarms.at(0).translations.size() == 1); - auto iterator = alarms.at(0).translations.find("es"); + expect(alarms.at(0).translations.size() == 2); // english and spanish + iterator = alarms.at(0).translations.find("es"); expect(iterator != alarms.at(0).translations.end()); expect(iterator->second.description == "some spanish maybe"); expect(iterator->second.details == "this is is also spanish believe me please"); - alarm_db.add_alarm_translation(alarm_db.list_alarms()[0].sha1sum, "is", "Some icelandic really", + alarm_db.add_alarm_translation(alarm_db.list_alarms().at(0).sha1sum, alarm_db.list_alarms().at(0).alarm_id, "is", "Some icelandic really", "This is really some icelandic"); alarms = alarm_db.list_alarms(); expect(alarms.size() == 1); - expect(alarms.at(0).translations.size() == 2); + expect(alarms.at(0).translations.size() == 3); // english, spanish and icelandic iterator = alarms.at(0).translations.find("is"); expect(iterator != alarms.at(0).translations.end()); expect(iterator->second.description == "Some icelandic really"); expect(iterator->second.details == "This is really some icelandic"); + + // Verify there is no sql logic error + auto activations = alarm_db.list_activations( + "es", 0, 10, tfc::snitch::level_e::unknown, tfc::snitch::api::active_e::all, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); + expect(activations.size() == 0); + + activations = alarm_db.list_activations( + "es", 0, 10, tfc::snitch::level_e::unknown, tfc::snitch::api::active_e::active, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); + expect(activations.size() == 0); + + activations = alarm_db.list_activations( + "es", 0, 10, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); + expect(activations.size() == 0); + + activations = alarm_db.list_activations( + "es", 0, 10, tfc::snitch::level_e::info, tfc::snitch::api::active_e::all, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); + expect(activations.size() == 0); + + // Add some activations + alarm_db.set_alarm(insert_id, {}); + // Insert an invalid activation + expect(throws([&]{alarm_db.set_alarm(999, {}); })); }; } diff --git a/libs/snitch/inc/public/tfc/snitch/common.hpp b/libs/snitch/inc/public/tfc/snitch/common.hpp index 8e0d52d036..41fdbc4d24 100644 --- a/libs/snitch/inc/public/tfc/snitch/common.hpp +++ b/libs/snitch/inc/public/tfc/snitch/common.hpp @@ -6,8 +6,9 @@ namespace tfc::snitch { -enum struct level_e : std::uint8_t { - unknown = 0, +enum struct level_e : std::int8_t { + all = -1, + unknown, info, warning, error, @@ -18,7 +19,7 @@ namespace api { using time_point = std::chrono::time_point; using alarm_id_t = std::int64_t; -enum struct active_e : std::int32_t { +enum struct active_e : std::int8_t { all = -1, inactive = 0, active = 1 @@ -27,8 +28,6 @@ enum struct active_e : std::int32_t { struct alarm { std::uint64_t alarm_id; std::string tfc_id; - std::string description; - std::string details; std::string sha1sum; level_e lvl{ level_e::unknown }; bool latching{}; @@ -41,8 +40,11 @@ struct alarm { }; struct activation { + std::uint64_t alarm_id; + std::uint64_t activation_id; std::string description; std::string details; + std::string locale; bool active{}; level_e lvl{ level_e::unknown }; bool latching{}; From ad0556fffda68f94e7996acf7f850bc194d6ae5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Wed, 19 Jun 2024 12:54:54 +0000 Subject: [PATCH 13/42] Inital formatting complete --- exes/themis/inc/alarm_database.hpp | 138 ++++++++++++--------- exes/themis/tests/themis_database_test.cpp | 87 ++++++++++++- 2 files changed, 161 insertions(+), 64 deletions(-) diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index e628a787c9..bb950a7513 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -53,17 +54,20 @@ CREATE TABLE IF NOT EXISTS AlarmTranslations( sha1sum TEXT NOT NULL, alarm_id INTEGER NOT NULL, locale TEXT NOT NULL, - short_msg TEXT NOT NULL, - msg TEXT NOT NULL, + details TEXT NOT NULL, + description TEXT NOT NULL, PRIMARY KEY(sha1sum, locale) ON CONFLICT REPLACE FOREIGN KEY(alarm_id) REFERENCES Alarms(alarm_id) ); )"; + // SQLITE is creating an automatic index on queries. Create it manually + // Mostly to avoid the log messages + db_ << "CREATE INDEX IF NOT EXISTS locale_idx ON AlarmTranslations(locale);"; // Timestamp stored as long integer to get millisecond precision // And because sqlite does not offer a native timestamp type. db_ << R"( CREATE TABLE IF NOT EXISTS AlarmActivations( - activation_id LONG INTEGER PRIMARY KEY, + activation_id INTEGER PRIMARY KEY, alarm_id INTEGER NOT NULL, activation_time LONG INTEGER NOT NULL, activation_level BOOLEAN NOT NULL, @@ -80,7 +84,7 @@ CREATE TABLE IF NOT EXISTS AlarmAcks( )"; db_ << R"( CREATE TABLE IF NOT EXISTS AlarmVariables( - activation_id INTEGER, + activation_id INTEGER NOT NULL, variable_key TEXT NOT NULL, variable_value TEXT NOT NULL, FOREIGN KEY(activation_id) REFERENCES AlarmActivations(activation_id) @@ -90,18 +94,18 @@ CREATE TABLE IF NOT EXISTS AlarmVariables( /** * @brief Register an alarm in the database * @param tfc_id The TFC ID of the alarm - * @param msg The message of the alarm - * @param short_msg The short message of the alarm + * @param description The message of the alarm + * @param details The short message of the alarm * @param latching Whether the alarm is latching * @param alarm_level The alarm level * @return The alarm ID */ [[nodiscard]] auto register_alarm_en(std::string_view tfc_id, - std::string_view msg, - std::string_view short_msg, + std::string_view description, + std::string_view details, bool latching, tfc::snitch::level_e alarm_level) -> std::uint64_t { - std::string sha1_ascii = get_sha1(fmt::format("{}{}", msg, short_msg)); + std::string sha1_ascii = get_sha1(fmt::format("{}{}", description, details)); std::uint64_t alarm_id = 0; try { db_ << "BEGIN;"; @@ -114,7 +118,7 @@ CREATE TABLE IF NOT EXISTS AlarmVariables( throw std::runtime_error("Failed to insert alarm into database"); } alarm_id = static_cast(insert_id); - add_alarm_translation(sha1_ascii, alarm_id, "en", short_msg, msg); + add_alarm_translation(sha1_ascii, alarm_id, "en", description, details); db_ << "COMMIT;"; } catch (std::exception& e) { // Rollback the transaction and rethrow @@ -134,8 +138,8 @@ SELECT alarm_level, Alarms.alarm_latching, AlarmTranslations.locale, - AlarmTranslations.short_msg, - AlarmTranslations.msg + AlarmTranslations.description, + AlarmTranslations.details FROM Alarms LEFT JOIN AlarmTranslations ON Alarms.sha1sum = AlarmTranslations.sha1sum; @@ -143,7 +147,7 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; // Accept the second table paramters as unique ptr's as the values can be null, and we want to preserve that db_ << query >> [&](std::uint64_t alarm_id, std::string tfc_id, std::string sha1sum, std::uint8_t alarm_level, bool alarm_latching, std::optional locale, - std::optional translated_short_msg, std::optional translated_msg) { + std::optional translated_description, std::optional translated_details) { auto iterator = alarms.find(alarm_id); if (iterator == alarms.end()) { alarms.insert( @@ -151,8 +155,9 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; tfc::snitch::api::alarm{ alarm_id, tfc_id, sha1sum, static_cast(alarm_level), alarm_latching, {} } }); } - if (locale.has_value() && translated_short_msg.has_value() && translated_msg.has_value()) { - alarms[alarm_id].translations.insert({ locale.value(), { translated_short_msg.value(), translated_msg.value() } }); + if (locale.has_value() && translated_details.has_value() && translated_description.has_value()) { + alarms[alarm_id].translations.insert( + { locale.value(), { translated_description.value(), translated_details.value() } }); } }; std::vector alarm_list; @@ -165,29 +170,35 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; auto add_alarm_translation(std::string_view sha1sum, std::uint64_t alarm_id, std::string_view locale, - std::string_view short_msg, - std::string_view msg) -> void { + std::string_view description, + std::string_view details) -> void { auto query = fmt::format( - "INSERT INTO AlarmTranslations(sha1sum, alarm_id, locale, short_msg, msg) VALUES('{}', {}, '{}','{}','{}');", - sha1sum, alarm_id, locale, short_msg, msg); - std::cerr << query << std::endl; + "INSERT INTO AlarmTranslations(sha1sum, alarm_id, locale, description, details) VALUES('{}', {}, '{}','{}','{}');", + sha1sum, alarm_id, locale, description, details); db_ << query; } auto set_alarm(std::uint64_t alarm_id, std::unordered_map variables, std::optional tp = {}) -> void { - db_ << fmt::format("INSERT INTO AlarmActivations(alarm_id, activation_time, activation_level) VALUES({},{},1)", alarm_id, - milliseconds_since_epoch(tp)); - std::int64_t last_insert_rowid = db_.last_insert_rowid(); - if (last_insert_rowid < 0) { - throw std::runtime_error("Failed to insert activation into database"); - } + db_ << "BEGIN;"; + try { + db_ << fmt::format("INSERT INTO AlarmActivations(alarm_id, activation_time, activation_level) VALUES({},{},1)", + alarm_id, milliseconds_since_epoch(tp)); + std::int64_t last_insert_rowid = db_.last_insert_rowid(); + if (last_insert_rowid < 0) { + throw std::runtime_error("Failed to insert activation into database"); + } - for (auto& [key, value] : variables) { - db_ << fmt::format("INSERT INTO AlarmVariables(activation_id, variable_key, variable_value) VALUES({},{},'{}');", - last_insert_rowid, key, value); + for (auto& [key, value] : variables) { + db_ << fmt::format("INSERT INTO AlarmVariables(activation_id, variable_key, variable_value) VALUES({},'{}','{}');", + last_insert_rowid, key, value); + } + } catch (std::exception& e) { + db_ << "ROLLBACK;"; + throw e; } + db_ << "COMMIT;"; } auto reset_alarm(std::uint64_t alarm_id, std::optional tp = {}) -> void { db_ << fmt::format("INSERT INTO AlarmActivations(alarm_id, activation_time, activation_level) VALUES({},{},0)", alarm_id, @@ -220,15 +231,20 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; Alarms.alarm_id, activation_time, activation_level, - AlarmTranslations.short_msg, - AlarmTranslations.msg, - AlarmTranslations.locale, + primary_text.details, + primary_text.description, + backup_text.details, + backup_text.description, Alarms.alarm_latching, Alarms.alarm_level FROM AlarmActivations JOIN Alarms on (Alarms.alarm_id = AlarmActivations.alarm_id) -LEFT OUTER JOIN AlarmTranslations on (Alarms.sha1sum = AlarmTranslations.sha1sum) -WHERE (AlarmTranslations.locale = '{}' OR AlarmTranslations.locale = 'en') AND activation_time >= {} AND activation_time <= {})", +-- Join the table twice, if the primary text is not populated. fallback to backup. +-- Joining the table twice over has the added benefit of not having to use a subquery. +-- and a result for a single Activation will always be a single row. +LEFT OUTER JOIN AlarmTranslations as primary_text on (Alarms.alarm_id = primary_text.alarm_id and primary_text.locale = '{}') +LEFT OUTER JOIN AlarmTranslations as backup_text on (Alarms.alarm_id = backup_text.alarm_id and backup_text.locale = 'en') +WHERE activation_time >= {} AND activation_time <= {})", locale, milliseconds_since_epoch(start), milliseconds_since_epoch(end)); if (level != tfc::snitch::level_e::unknown) { populated_query += fmt::format(" AND alarm_level = {}", static_cast(level)); @@ -238,45 +254,45 @@ WHERE (AlarmTranslations.locale = '{}' OR AlarmTranslations.locale = 'en') AND a } populated_query += fmt::format(" LIMIT {} OFFSET {};", count, start_count); - std::unordered_map activations; + std::vector activations; db_ << populated_query >> [&](std::uint64_t activation_id, std::uint64_t alarm_id, std::int64_t activation_time, - bool activation_level, std::optional translated_short_msg, - std::optional translated_msg, std::optional tlocale, + bool activation_level, std::optional primary_details, + std::optional primary_description, std::optional backup_details, + std::optional backup_description, std::optional tlocale, bool alarm_latching, std::uint8_t alarm_level) { - // This callback can be called multiple times for the same activation_id - // Once with the locale requested and once with the english locale - // I am going to try and solve that with a group by clause - - // For now we do this in c++, if we have value and the underlying element is not found or there is no element and the locale is wrong - if (translated_msg.has_value() && translated_short_msg.has_value() && tlocale.has_value()) { - auto iterator = activations.find(activation_id); - if (iterator != activations.end() || iterator->second.locale == locale) { - activations[activation_id] = { alarm_id, - activation_id, - translated_msg.value(), - translated_short_msg.value(), - tlocale.value(), - activation_level, - static_cast(alarm_level), - alarm_latching, - timepoint_from_milliseconds(activation_time) }; - } + if (!backup_description.has_value() || !backup_details.has_value()) { + throw std::runtime_error("Backup message not found for alarm translation. This should never happen."); } + std::string details = primary_details.value_or(backup_details.value()); + std::string description = primary_description.value_or(backup_description.value()); + activations.emplace_back(alarm_id, activation_id, description, details, tlocale.value(), + activation_level, static_cast(alarm_level), alarm_latching, + timepoint_from_milliseconds(activation_time)); }; - std::vector ret_value; - for (auto& [_, activation] : activations) { - ret_value.push_back(activation); + for(auto& activation : activations) { + // TODO: This is not great would be better to do this in a large query + // As of now we are doing a query for each activation to get the variables + std::vector> variables; + db_ << fmt::format("SELECT variable_key, variable_value FROM AlarmVariables WHERE activation_id = {};", + activation.activation_id) >> + [&](std::string key, std::string value) { variables.emplace_back(key, value); }; + fmt::dynamic_format_arg_store store; + for (auto& [key, value] : variables) { + store.push_back(fmt::arg(key.c_str(), value)); + } + activation.details = fmt::vformat(activation.details, store); + activation.description = fmt::vformat(activation.description, store); } - return ret_value; + return activations; } // Note. Use `echo -n "value" | sha1sum` to not hash the newline character and // match the results from this function. - [[nodiscard]] static auto get_sha1(std::string_view msg) -> std::string { + [[nodiscard]] static auto get_sha1(std::string_view value) -> std::string { // Calculate sha1 std::array sha1_res; - SHA1(reinterpret_cast(msg.data()), msg.size(), sha1_res.data()); + SHA1(reinterpret_cast(value.data()), value.size(), sha1_res.data()); // Convert sha1 to ascii std::string sha1_ascii; diff --git a/exes/themis/tests/themis_database_test.cpp b/exes/themis/tests/themis_database_test.cpp index 8effbfc37b..209a48801f 100644 --- a/exes/themis/tests/themis_database_test.cpp +++ b/exes/themis/tests/themis_database_test.cpp @@ -42,8 +42,8 @@ auto main(int argc, char** argv) -> int { expect(alarms.at(0).alarm_id == insert_id); auto iterator = alarms.at(0).translations.find("en"); expect(iterator != alarms.at(0).translations.end()); - expect(iterator->second.details == "msg") << iterator->second.description; - expect(iterator->second.description == "short msg") << iterator->second.details; + expect(iterator->second.description == "msg") << iterator->second.description; + expect(iterator->second.details == "short msg") << iterator->second.details; // Translations are attached to alarms alarm_db.add_alarm_translation(alarm_db.list_alarms().at(0).sha1sum, alarm_db.list_alarms().at(0).alarm_id, "es", "some spanish maybe", @@ -92,8 +92,89 @@ auto main(int argc, char** argv) -> int { expect(activations.size() == 0); // Add some activations - alarm_db.set_alarm(insert_id, {}); + for(int i = 0; i < 10; i++){ + alarm_db.set_alarm(insert_id, {}); + alarm_db.reset_alarm(insert_id); + } // Insert an invalid activation expect(throws([&]{alarm_db.set_alarm(999, {}); })); + + // Verify our inserts + activations = alarm_db.list_activations( + "es", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::all, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); + expect(activations.size() == 20) << activations.size(); + activations = alarm_db.list_activations( + "es", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); + expect(activations.size() == 10) << activations.size(); + activations = alarm_db.list_activations( + "es", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::inactive, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); + expect(activations.size() == 10) << activations.size(); + + }; + "Fallback to en if locale is not available"_test = []{ + tfc::themis::alarm_database alarm_db(true); + auto insert_id = alarm_db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); + alarm_db.add_alarm_translation(alarm_db.list_alarms().at(0).sha1sum, alarm_db.list_alarms().at(0).alarm_id, "es", "spanish description", "spanish details"); + alarm_db.set_alarm(insert_id, {}); + auto activations = alarm_db.list_activations( + "is", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); + expect(activations.size() == 1); + expect(activations.at(0).description == "description") << activations.at(0).description; + expect(activations.at(0).details == "details") << activations.at(0).details; + + activations = alarm_db.list_activations( + "es", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); + expect(activations.size() == 1); + expect(activations.at(0).description == "spanish description") << activations.at(0).description; + expect(activations.at(0).details == "spanish details") << activations.at(0).details; + }; + "String formatting with variables"_test = []{ + tfc::themis::alarm_database alarm_db(true); + // Add a variable alarm + auto var_alarm_id = alarm_db.register_alarm_en("tfc_id", "{var}", "{var}", false, tfc::snitch::level_e::info); + expect(alarm_db.list_alarms().size() == 1); + alarm_db.set_alarm(var_alarm_id, {{ "var", "10.0"}}); + auto activations = alarm_db.list_activations( + "en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); + expect(activations.size() == 1); + expect(activations.at(0).alarm_id == var_alarm_id); + expect(activations.at(0).description == "10.0"); + expect(activations.at(0).details == "10.0"); + + var_alarm_id = alarm_db.register_alarm_en("tfc_id", "description {var} {var2} {var3}", "details {var} {var2} {var3}", false, tfc::snitch::level_e::warning); + expect(alarm_db.list_alarms().size() == 2); + alarm_db.set_alarm(var_alarm_id, {{ "var", "10.0"}, {"var2", "20.0"}, {"var3", "30.0"}}); + activations = alarm_db.list_activations( + "en", 0, 10000, tfc::snitch::level_e::warning, tfc::snitch::api::active_e::active, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); + expect(activations.size() == 1); + expect(activations.at(0).alarm_id == var_alarm_id); + expect(activations.at(0).description == "description 10.0 20.0 30.0") << activations.at(0).description; + expect(activations.at(0).details == "details 10.0 20.0 30.0") << activations.at(0).details; + }; + "ACK the alarm"_test = []{ + //TODO: Do something here. + tfc::themis::alarm_database alarm_db(true); + auto insert_id = alarm_db.register_alarm_en("tfc_id", "description", "details", true, tfc::snitch::level_e::info); + alarm_db.set_alarm(insert_id, {}); + alarm_db.ack_alarm(insert_id); + auto activations = alarm_db.list_activations( + "en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); + expect(activations.size() == 1); }; } From 85a1d07d2297c927e30ea36a22f766d5c5f73437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Wed, 19 Jun 2024 13:32:48 +0000 Subject: [PATCH 14/42] Fixed ordering of list_alarms, added spanish variable test --- exes/themis/inc/alarm_database.hpp | 13 ++++++------ exes/themis/tests/themis_database_test.cpp | 23 +++++++++++++++++++--- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index bb950a7513..dd6e355831 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace tfc::themis { @@ -118,7 +119,7 @@ CREATE TABLE IF NOT EXISTS AlarmVariables( throw std::runtime_error("Failed to insert alarm into database"); } alarm_id = static_cast(insert_id); - add_alarm_translation(sha1_ascii, alarm_id, "en", description, details); + add_alarm_translation(alarm_id, "en", description, details); db_ << "COMMIT;"; } catch (std::exception& e) { // Rollback the transaction and rethrow @@ -129,7 +130,8 @@ CREATE TABLE IF NOT EXISTS AlarmVariables( } [[nodiscard]] auto list_alarms() -> std::vector { - std::unordered_map alarms; + // std::map to maintain alarm order. + std::map alarms; std::string query = R"( SELECT Alarms.alarm_id, @@ -167,14 +169,13 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; return alarm_list; } - auto add_alarm_translation(std::string_view sha1sum, - std::uint64_t alarm_id, + auto add_alarm_translation(std::uint64_t alarm_id, std::string_view locale, std::string_view description, std::string_view details) -> void { auto query = fmt::format( - "INSERT INTO AlarmTranslations(sha1sum, alarm_id, locale, description, details) VALUES('{}', {}, '{}','{}','{}');", - sha1sum, alarm_id, locale, description, details); + "INSERT INTO AlarmTranslations(sha1sum, alarm_id, locale, description, details) SELECT DISTINCT sha1sum, {}, '{}','{}','{}' FROM Alarms where alarm_id = {}", + alarm_id, locale, description, details, alarm_id); db_ << query; } diff --git a/exes/themis/tests/themis_database_test.cpp b/exes/themis/tests/themis_database_test.cpp index 209a48801f..3f18173130 100644 --- a/exes/themis/tests/themis_database_test.cpp +++ b/exes/themis/tests/themis_database_test.cpp @@ -46,7 +46,7 @@ auto main(int argc, char** argv) -> int { expect(iterator->second.details == "short msg") << iterator->second.details; // Translations are attached to alarms - alarm_db.add_alarm_translation(alarm_db.list_alarms().at(0).sha1sum, alarm_db.list_alarms().at(0).alarm_id, "es", "some spanish maybe", + alarm_db.add_alarm_translation(alarm_db.list_alarms().at(0).alarm_id, "es", "some spanish maybe", "this is is also spanish believe me please"); alarms = alarm_db.list_alarms(); expect(alarms.size() == 1); @@ -56,7 +56,7 @@ auto main(int argc, char** argv) -> int { expect(iterator->second.description == "some spanish maybe"); expect(iterator->second.details == "this is is also spanish believe me please"); - alarm_db.add_alarm_translation(alarm_db.list_alarms().at(0).sha1sum, alarm_db.list_alarms().at(0).alarm_id, "is", "Some icelandic really", + alarm_db.add_alarm_translation(alarm_db.list_alarms().at(0).alarm_id, "is", "Some icelandic really", "This is really some icelandic"); alarms = alarm_db.list_alarms(); expect(alarms.size() == 1); @@ -120,7 +120,7 @@ auto main(int argc, char** argv) -> int { "Fallback to en if locale is not available"_test = []{ tfc::themis::alarm_database alarm_db(true); auto insert_id = alarm_db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); - alarm_db.add_alarm_translation(alarm_db.list_alarms().at(0).sha1sum, alarm_db.list_alarms().at(0).alarm_id, "es", "spanish description", "spanish details"); + alarm_db.add_alarm_translation(alarm_db.list_alarms().at(0).alarm_id, "es", "spanish description", "spanish details"); alarm_db.set_alarm(insert_id, {}); auto activations = alarm_db.list_activations( "is", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, @@ -155,6 +155,7 @@ auto main(int argc, char** argv) -> int { var_alarm_id = alarm_db.register_alarm_en("tfc_id", "description {var} {var2} {var3}", "details {var} {var2} {var3}", false, tfc::snitch::level_e::warning); expect(alarm_db.list_alarms().size() == 2); + alarm_db.add_alarm_translation(var_alarm_id, "es", "spanish description {var} {var2} {var3}", "spanish details {var} {var2} {var3}"); alarm_db.set_alarm(var_alarm_id, {{ "var", "10.0"}, {"var2", "20.0"}, {"var3", "30.0"}}); activations = alarm_db.list_activations( "en", 0, 10000, tfc::snitch::level_e::warning, tfc::snitch::api::active_e::active, @@ -164,6 +165,22 @@ auto main(int argc, char** argv) -> int { expect(activations.at(0).alarm_id == var_alarm_id); expect(activations.at(0).description == "description 10.0 20.0 30.0") << activations.at(0).description; expect(activations.at(0).details == "details 10.0 20.0 30.0") << activations.at(0).details; + activations = alarm_db.list_activations( + "es", 0, 10000, tfc::snitch::level_e::warning, tfc::snitch::api::active_e::active, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); + expect(activations.size() == 1); + expect(activations.at(0).alarm_id == var_alarm_id); + expect(activations.at(0).description == "spanish description 10.0 20.0 30.0") << activations.at(0).description; + expect(activations.at(0).details == "spanish details 10.0 20.0 30.0") << activations.at(0).details; + activations = alarm_db.list_activations( + "is", 0, 10000, tfc::snitch::level_e::warning, tfc::snitch::api::active_e::active, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); + expect(activations.size() == 1); + expect(activations.at(0).alarm_id == var_alarm_id); + expect(activations.at(0).description == "description 10.0 20.0 30.0") << activations.at(0).description; + expect(activations.at(0).details == "details 10.0 20.0 30.0") << activations.at(0).details; }; "ACK the alarm"_test = []{ //TODO: Do something here. From 912e62fb7e9c9784dc133ccd7bf4ea26ce70fe99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Thu, 20 Jun 2024 10:37:47 +0000 Subject: [PATCH 15/42] Dbus interface outlines added --- exes/themis/CMakeLists.txt | 4 + exes/themis/inc/alarm_database.hpp | 2 +- exes/themis/inc/dbus_interface.hpp | 87 ++++++++++++++++++++ exes/themis/src/main.cpp | 15 ++++ libs/snitch/inc/public/tfc/snitch/common.hpp | 18 ++++ 5 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 exes/themis/inc/dbus_interface.hpp diff --git a/exes/themis/CMakeLists.txt b/exes/themis/CMakeLists.txt index 19fb438a7e..6c6e908134 100644 --- a/exes/themis/CMakeLists.txt +++ b/exes/themis/CMakeLists.txt @@ -7,6 +7,8 @@ find_package(PkgConfig REQUIRED) pkg_check_modules(SDBUSPLUS REQUIRED IMPORTED_TARGET GLOBAL sdbusplus) find_package(unofficial-sqlite3 CONFIG REQUIRED) +find_package(OpenSSL CONFIG REQUIRED) + find_path(SQLITE_MODERN_CPP_INCLUDE_DIRS "sqlite_modern_cpp.h") target_include_directories(themis PRIVATE @@ -22,9 +24,11 @@ target_link_libraries(themis tfc::confman tfc::logger tfc::dbus_util + tfc::snitch Boost::program_options PkgConfig::SDBUSPLUS unofficial::sqlite3::sqlite3 + OpenSSL::Crypto ) add_subdirectory(tests) diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index dd6e355831..fdfea5ec64 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -180,7 +180,7 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; } auto set_alarm(std::uint64_t alarm_id, - std::unordered_map variables, + const std::unordered_map& variables, std::optional tp = {}) -> void { db_ << "BEGIN;"; try { diff --git a/exes/themis/inc/dbus_interface.hpp b/exes/themis/inc/dbus_interface.hpp new file mode 100644 index 0000000000..fafbb29a03 --- /dev/null +++ b/exes/themis/inc/dbus_interface.hpp @@ -0,0 +1,87 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace tfc::themis { +using tfc::ipc::details::type_e; + +using dbus_error = tfc::dbus::exception::runtime; +using namespace tfc::snitch::api::dbus; + +class interface { +public: + explicit interface(boost::asio::io_context& ctx, tfc::themis::alarm_database& database) { + connection_ = std::make_shared(ctx, tfc::dbus::sd_bus_open_system()); + object_server_ = std::make_unique(connection_); + connection_->request_name(interface_name.data()); + dbus_interface_ = + object_server_->add_unique_interface(object_path.data(), interface_name.data()); + + dbus_interface_->register_method(std::string(methods::list_alarms), + [&]() -> std::string { + return glz::write_json(database.list_alarms()); + }); + + dbus_interface_->register_method(std::string(methods::register_alarm), + [&](std::string tfc_id, const std::string& description, const std::string& details, bool latching, int alarm_level) -> std::uint64_t { + // TODO: User supplied alarm_level needs more verification + return database.register_alarm_en(tfc_id, description, details, latching, static_cast(alarm_level)); + }); + + dbus_interface_->register_method(std::string(methods::set_alarm), + [&](std::uint64_t alarm_id, const std::unordered_map& args) -> void { + database.set_alarm(alarm_id, args); + }); + dbus_interface_->register_method(std::string(methods::reset_alarm), + [&](std::uint64_t alarm_id) -> void { + database.reset_alarm(alarm_id); + }); + dbus_interface_->register_method(std::string(methods::ack_alarm), + [&](std::uint64_t alarm_id) -> void { + database.ack_alarm(alarm_id); + }); + dbus_interface_->register_method(std::string(methods::ack_all_alarms), + [&]() -> void { + database.ack_all_alarms(); + }); + dbus_interface_->register_method(std::string(methods::list_activations), + [&](const std::string& locale, std::uint64_t start_count, std::uint64_t count, int alarm_level, int active, int64_t start, int64_t end) -> std::string { + auto cstart = tfc::themis::alarm_database::timepoint_from_milliseconds(start); + auto cend = tfc::themis::alarm_database::timepoint_from_milliseconds(end); + return glz::write_json(database.list_activations(locale, start_count, count, static_cast(alarm_level), static_cast(active), cstart, cend)); + }); + + // Signal alarm_id, current_activation, ack_status + dbus_interface_->register_signal>(std::string(signals::alarm_changed)); + dbus_interface_->initialize(); + } + +private: + std::shared_ptr connection_; + std::unique_ptr dbus_interface_; + std::unique_ptr object_server_; +}; +} // namespace tfc::ipc_ruler diff --git a/exes/themis/src/main.cpp b/exes/themis/src/main.cpp index 2737b5f0e4..aeb444de73 100644 --- a/exes/themis/src/main.cpp +++ b/exes/themis/src/main.cpp @@ -1,14 +1,29 @@ #include +#include #include #include #include +#include +#include + namespace po = boost::program_options; +namespace asio = boost::asio; auto main(int argc, char** argv) -> int { auto description{ tfc::base::default_description() }; bool in_memory = false; description.add_options()("memory", po::bool_switch(&in_memory), "Run the database in memory, non persistent"); tfc::base::init(argc, argv, description); + + asio::io_context ctx; + + // Initialize the database + tfc::themis::alarm_database db(in_memory); + + // Initialize the IPC server + tfc::themis::interface i(ctx, db); + + ctx.run(); return 0; } diff --git a/libs/snitch/inc/public/tfc/snitch/common.hpp b/libs/snitch/inc/public/tfc/snitch/common.hpp index 41fdbc4d24..e0efbf344a 100644 --- a/libs/snitch/inc/public/tfc/snitch/common.hpp +++ b/libs/snitch/inc/public/tfc/snitch/common.hpp @@ -50,7 +50,25 @@ struct activation { bool latching{}; time_point timestamp; }; +namespace dbus { +static constexpr std::string_view interface_name = "com.skaginn3x.Alarm"; +static constexpr std::string_view object_path = "/com/skaginn3x/Alarm"; +namespace properties { +} +namespace methods { + static constexpr std::string_view register_alarm = "RegisterAlarm"; + static constexpr std::string_view list_alarms = "ListAlarms"; + static constexpr std::string_view list_activations = "ListActivations"; + static constexpr std::string_view set_alarm = "SetAlarm"; + static constexpr std::string_view reset_alarm = "ResetAlarm"; + static constexpr std::string_view ack_alarm = "AckAlarm"; + static constexpr std::string_view ack_all_alarms = "AckAllAlarms"; +} +namespace signals { + static constexpr std::string_view alarm_changed = "AlarmChanged"; +} +} } // namespace api } // namespace tfc::snitch From 8652b7ec5f63772bc815fffe86ae0ccd6efc7611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Thu, 20 Jun 2024 12:16:50 +0000 Subject: [PATCH 16/42] Align code better to documentation, update documentation, remove ack --- docs/design/snitch.md | 10 ++++---- exes/themis/inc/alarm_database.hpp | 24 ++------------------ exes/themis/inc/dbus_interface.hpp | 15 ++++++++---- exes/themis/tests/themis_database_test.cpp | 14 ++---------- libs/snitch/inc/public/tfc/snitch/common.hpp | 9 +++++--- 5 files changed, 26 insertions(+), 46 deletions(-) diff --git a/docs/design/snitch.md b/docs/design/snitch.md index 99ac3c6e22..56f37302ac 100644 --- a/docs/design/snitch.md +++ b/docs/design/snitch.md @@ -19,17 +19,19 @@ dynamic variable count must be the same. The alarm system is exposed through the `com.skaginn3x.Alarm` interface. ### Methods - RegisterAlarm (s: alarm_name, i: alarm_level, b: non_latching) -> i: alarm_id Note errors can only be latched + RegisterAlarm (s: tfc_id, i: alarm_level, b: latching) -> i: alarm_id Note errors can only be latched # ListAlarms returns boolean true in_locale if translation exists otherwise english ListAlarms () -> s -> json of: std::vector ; }> # Note active = -1 for all, alarm_level = -1 for all a max size of 100 alarms will be sent at a time ListActivations (s: locale, i: start_count, i: count, i: alarm_level, i: active, x: startunixTimestamp, x: endUnixTimestamp) -> s -> json of: struct { string description; string details; bool latching; enum alarm_level; bool active; std::uint64_t millisec_from_epoch; }; SetAlarm(i: alarm_id, as: variables) ResetAlarm(i: alarm_id) - AckAlarm(i: alarm_id) - AckAllAlarms() + TryReset(i: alarm_id) # Transmits a signal to the alarm to reset itself + TryResetAll() # Transmits a signal to all alarms to reset themselfs ### Signals - AlarmChanged(i: alarm_id, b: current_activation, b: ack_status) + AlarmActivationChanged(i: alarm_id, b: current_activation) + TryReset(i: alarm_id) + TryResetAll() ### Properties As of now there are no properties ## Database schema diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index fdfea5ec64..fa8bbe9058 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -74,14 +74,6 @@ CREATE TABLE IF NOT EXISTS AlarmActivations( activation_level BOOLEAN NOT NULL, FOREIGN KEY(alarm_id) REFERENCES Alarms(alarm_id) ); -)"; - db_ << R"( -CREATE TABLE IF NOT EXISTS AlarmAcks( - activation_id INTEGER, - ack_timestamp LONG INTEGER NOT NULL, - UNIQUE(activation_id) ON CONFLICT IGNORE, - FOREIGN KEY(activation_id) REFERENCES AlarmActivations(activation_id) -); )"; db_ << R"( CREATE TABLE IF NOT EXISTS AlarmVariables( @@ -206,19 +198,6 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; milliseconds_since_epoch(tp)); } - auto ack_alarm(std::uint64_t activation_id, std::optional tp = {}) -> void { - db_ << fmt::format("INSERT INTO AlarmAcks(activation_id, ack_timestamp) VALUES({},{});", activation_id, - milliseconds_since_epoch(tp)); - } - - auto ack_all_alarms(std::optional tp = {}) -> void { - // TODO: This is not 100% but we should be able to do this in a single query. Very fast. - // Gonna build up some more elaborate test cases and dig into this when that is complete - db_ << fmt::format( - "INSERT INTO AlarmAcks(activation_id, ack_timestamp) SELECT DISTINCT activation_id, {} FROM AlarmActivations;", - milliseconds_since_epoch(tp)); - } - [[nodiscard]] auto list_activations(std::string_view locale, std::uint64_t start_count, std::uint64_t count, @@ -267,9 +246,10 @@ WHERE activation_time >= {} AND activation_time <= {})", } std::string details = primary_details.value_or(backup_details.value()); std::string description = primary_description.value_or(backup_description.value()); + bool in_locale = primary_description.has_value() && primary_details.has_value(); activations.emplace_back(alarm_id, activation_id, description, details, tlocale.value(), activation_level, static_cast(alarm_level), alarm_latching, - timepoint_from_milliseconds(activation_time)); + timepoint_from_milliseconds(activation_time), in_locale); }; for(auto& activation : activations) { // TODO: This is not great would be better to do this in a large query diff --git a/exes/themis/inc/dbus_interface.hpp b/exes/themis/inc/dbus_interface.hpp index fafbb29a03..4668f5f696 100644 --- a/exes/themis/inc/dbus_interface.hpp +++ b/exes/themis/inc/dbus_interface.hpp @@ -59,13 +59,16 @@ class interface { [&](std::uint64_t alarm_id) -> void { database.reset_alarm(alarm_id); }); - dbus_interface_->register_method(std::string(methods::ack_alarm), + dbus_interface_->register_method(std::string(methods::try_reset), [&](std::uint64_t alarm_id) -> void { - database.ack_alarm(alarm_id); + auto message = dbus_interface_->new_signal(signals::try_reset_all.data()); + message.append(alarm_id); + message.signal_send(); }); - dbus_interface_->register_method(std::string(methods::ack_all_alarms), + dbus_interface_->register_method(std::string(methods::try_reset_all), [&]() -> void { - database.ack_all_alarms(); + auto message = dbus_interface_->new_signal(signals::try_reset_all.data()); + message.signal_send(); }); dbus_interface_->register_method(std::string(methods::list_activations), [&](const std::string& locale, std::uint64_t start_count, std::uint64_t count, int alarm_level, int active, int64_t start, int64_t end) -> std::string { @@ -75,7 +78,9 @@ class interface { }); // Signal alarm_id, current_activation, ack_status - dbus_interface_->register_signal>(std::string(signals::alarm_changed)); + dbus_interface_->register_signal>(std::string(signals::alarm_activation_changed)); + dbus_interface_->register_signal(std::string(signals::try_reset)); + dbus_interface_->register_signal(std::string(signals::try_reset_all)); dbus_interface_->initialize(); } diff --git a/exes/themis/tests/themis_database_test.cpp b/exes/themis/tests/themis_database_test.cpp index 3f18173130..ab40163613 100644 --- a/exes/themis/tests/themis_database_test.cpp +++ b/exes/themis/tests/themis_database_test.cpp @@ -129,6 +129,7 @@ auto main(int argc, char** argv) -> int { expect(activations.size() == 1); expect(activations.at(0).description == "description") << activations.at(0).description; expect(activations.at(0).details == "details") << activations.at(0).details; + expect(activations.at(0).in_requested_locale == false); activations = alarm_db.list_activations( "es", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, @@ -137,6 +138,7 @@ auto main(int argc, char** argv) -> int { expect(activations.size() == 1); expect(activations.at(0).description == "spanish description") << activations.at(0).description; expect(activations.at(0).details == "spanish details") << activations.at(0).details; + expect(activations.at(0).in_requested_locale == true); }; "String formatting with variables"_test = []{ tfc::themis::alarm_database alarm_db(true); @@ -182,16 +184,4 @@ auto main(int argc, char** argv) -> int { expect(activations.at(0).description == "description 10.0 20.0 30.0") << activations.at(0).description; expect(activations.at(0).details == "details 10.0 20.0 30.0") << activations.at(0).details; }; - "ACK the alarm"_test = []{ - //TODO: Do something here. - tfc::themis::alarm_database alarm_db(true); - auto insert_id = alarm_db.register_alarm_en("tfc_id", "description", "details", true, tfc::snitch::level_e::info); - alarm_db.set_alarm(insert_id, {}); - alarm_db.ack_alarm(insert_id); - auto activations = alarm_db.list_activations( - "en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, - tfc::themis::alarm_database::timepoint_from_milliseconds(0), - tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); - expect(activations.size() == 1); - }; } diff --git a/libs/snitch/inc/public/tfc/snitch/common.hpp b/libs/snitch/inc/public/tfc/snitch/common.hpp index e0efbf344a..8c7da15370 100644 --- a/libs/snitch/inc/public/tfc/snitch/common.hpp +++ b/libs/snitch/inc/public/tfc/snitch/common.hpp @@ -49,6 +49,7 @@ struct activation { level_e lvl{ level_e::unknown }; bool latching{}; time_point timestamp; + bool in_requested_locale; }; namespace dbus { static constexpr std::string_view interface_name = "com.skaginn3x.Alarm"; @@ -62,11 +63,13 @@ namespace methods { static constexpr std::string_view list_activations = "ListActivations"; static constexpr std::string_view set_alarm = "SetAlarm"; static constexpr std::string_view reset_alarm = "ResetAlarm"; - static constexpr std::string_view ack_alarm = "AckAlarm"; - static constexpr std::string_view ack_all_alarms = "AckAllAlarms"; + static constexpr std::string_view try_reset = "TryReset"; + static constexpr std::string_view try_reset_all = "TryResetAll"; } namespace signals { - static constexpr std::string_view alarm_changed = "AlarmChanged"; + static constexpr std::string_view alarm_activation_changed = "AlarmActivationChanged"; + static constexpr std::string_view try_reset = methods::try_reset; + static constexpr std::string_view try_reset_all = methods::try_reset_all; } } } // namespace api From ce41d59eab850e829d03bc10d1ff2481eeba84c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Thu, 20 Jun 2024 12:42:09 +0000 Subject: [PATCH 17/42] Typo --- exes/themis/inc/dbus_interface.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/exes/themis/inc/dbus_interface.hpp b/exes/themis/inc/dbus_interface.hpp index 4668f5f696..0d75850be7 100644 --- a/exes/themis/inc/dbus_interface.hpp +++ b/exes/themis/inc/dbus_interface.hpp @@ -46,7 +46,7 @@ class interface { }); dbus_interface_->register_method(std::string(methods::register_alarm), - [&](std::string tfc_id, const std::string& description, const std::string& details, bool latching, int alarm_level) -> std::uint64_t { + [&](const sdbusplus::message_t& msg, std::string tfc_id, const std::string& description, const std::string& details, bool latching, int alarm_level) -> std::uint64_t { // TODO: User supplied alarm_level needs more verification return database.register_alarm_en(tfc_id, description, details, latching, static_cast(alarm_level)); }); @@ -61,7 +61,7 @@ class interface { }); dbus_interface_->register_method(std::string(methods::try_reset), [&](std::uint64_t alarm_id) -> void { - auto message = dbus_interface_->new_signal(signals::try_reset_all.data()); + auto message = dbus_interface_->new_signal(signals::try_reset.data()); message.append(alarm_id); message.signal_send(); }); @@ -88,5 +88,6 @@ class interface { std::shared_ptr connection_; std::unique_ptr dbus_interface_; std::unique_ptr object_server_; + std::unordered_map> dbus_ids_to_monitor; // Alarm members and monitoring parties }; } // namespace tfc::ipc_ruler From d73c57056feccb12abffe5911b22049a3d8cfb08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Thu, 20 Jun 2024 13:59:59 +0000 Subject: [PATCH 18/42] Added some basics of monitoring for a process that is lost. --- docs/design/snitch.md | 2 +- exes/themis/inc/dbus_interface.hpp | 73 ++++++++++++------- .../inc/public/tfc/dbus/match_rules.hpp | 17 +++++ 3 files changed, 63 insertions(+), 29 deletions(-) diff --git a/docs/design/snitch.md b/docs/design/snitch.md index 56f37302ac..e0d8cb3be8 100644 --- a/docs/design/snitch.md +++ b/docs/design/snitch.md @@ -60,7 +60,7 @@ Alarm variables ## Policy Executables must register their alarms on construction. Unregistered alarms cannot be notified. -Once an alarm is registered the Alarm with Themis he will monitor the NameLost signal for the +Once an alarm is registered the Alarm with Themis he will monitor the NameOwnerChanged signal for the name that registered the alarm. If the name is lost the alarm will be deactivated. If an alarm is registered having changed the level or latching attributes the alarm shall be diff --git a/exes/themis/inc/dbus_interface.hpp b/exes/themis/inc/dbus_interface.hpp index 0d75850be7..366aae9300 100644 --- a/exes/themis/inc/dbus_interface.hpp +++ b/exes/themis/inc/dbus_interface.hpp @@ -12,24 +12,13 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - namespace tfc::themis { using tfc::ipc::details::type_e; using dbus_error = tfc::dbus::exception::runtime; using namespace tfc::snitch::api::dbus; +using std::string_view_literals::operator""sv; class interface { public: @@ -37,40 +26,40 @@ class interface { connection_ = std::make_shared(ctx, tfc::dbus::sd_bus_open_system()); object_server_ = std::make_unique(connection_); connection_->request_name(interface_name.data()); - dbus_interface_ = + interface_ = object_server_->add_unique_interface(object_path.data(), interface_name.data()); - dbus_interface_->register_method(std::string(methods::list_alarms), + interface_->register_method(std::string(methods::list_alarms), [&]() -> std::string { return glz::write_json(database.list_alarms()); }); - dbus_interface_->register_method(std::string(methods::register_alarm), + interface_->register_method(std::string(methods::register_alarm), [&](const sdbusplus::message_t& msg, std::string tfc_id, const std::string& description, const std::string& details, bool latching, int alarm_level) -> std::uint64_t { // TODO: User supplied alarm_level needs more verification return database.register_alarm_en(tfc_id, description, details, latching, static_cast(alarm_level)); }); - dbus_interface_->register_method(std::string(methods::set_alarm), + interface_->register_method(std::string(methods::set_alarm), [&](std::uint64_t alarm_id, const std::unordered_map& args) -> void { database.set_alarm(alarm_id, args); }); - dbus_interface_->register_method(std::string(methods::reset_alarm), + interface_->register_method(std::string(methods::reset_alarm), [&](std::uint64_t alarm_id) -> void { database.reset_alarm(alarm_id); }); - dbus_interface_->register_method(std::string(methods::try_reset), + interface_->register_method(std::string(methods::try_reset), [&](std::uint64_t alarm_id) -> void { - auto message = dbus_interface_->new_signal(signals::try_reset.data()); + auto message = interface_->new_signal(signals::try_reset.data()); message.append(alarm_id); message.signal_send(); }); - dbus_interface_->register_method(std::string(methods::try_reset_all), + interface_->register_method(std::string(methods::try_reset_all), [&]() -> void { - auto message = dbus_interface_->new_signal(signals::try_reset_all.data()); + auto message = interface_->new_signal(signals::try_reset_all.data()); message.signal_send(); }); - dbus_interface_->register_method(std::string(methods::list_activations), + interface_->register_method(std::string(methods::list_activations), [&](const std::string& locale, std::uint64_t start_count, std::uint64_t count, int alarm_level, int active, int64_t start, int64_t end) -> std::string { auto cstart = tfc::themis::alarm_database::timepoint_from_milliseconds(start); auto cend = tfc::themis::alarm_database::timepoint_from_milliseconds(end); @@ -78,16 +67,44 @@ class interface { }); // Signal alarm_id, current_activation, ack_status - dbus_interface_->register_signal>(std::string(signals::alarm_activation_changed)); - dbus_interface_->register_signal(std::string(signals::try_reset)); - dbus_interface_->register_signal(std::string(signals::try_reset_all)); - dbus_interface_->initialize(); + interface_->register_signal>(std::string(signals::alarm_activation_changed)); + interface_->register_signal(std::string(signals::try_reset)); + interface_->register_signal(std::string(signals::try_reset_all)); + fmt::println(stderr, "{}", match_rule_); + name_lost_match_ = std::make_unique( + *connection_, match_rule_.data(), std::bind_front(&interface::match_callback, this)); + interface_->initialize(); + } private: + static constexpr std::string_view dbus_interface_ = "org.freedesktop.DBus"sv; + static constexpr std::string_view dbus_name_ = "org.freedesktop.DBus"sv; + static constexpr std::string_view dbus_path_ = "/org/freedesktop/DBus"sv; + static constexpr std::string_view name_owner_changed_ = "NameOwnerChanged"sv; + static constexpr std::string_view match_rule_ = tfc::dbus::match::rules::make_match_rule(); + auto match_callback(sdbusplus::message_t& msg) -> void { + std::string name; + std::string old_owner; + std::string new_owner; + msg.read(name, old_owner, new_owner); + if (name == old_owner){ + // Someone has left the chat. + auto iterator = dbus_ids_to_monitor_.find(name); + if (iterator != dbus_ids_to_monitor_.end()){ + // We knew that guy + //TODO: Decide what to do here. + //TODO: Could cleanup the alarm state, and place a warning into the logs that this process is not handling his alarms anymore. + //TODO: Could also just ignore it, and let the alarm state be as it is. + //TODO: There is also the question of alarms that are active when the system is shut down. Who cleans them up? + fmt::println(stderr, "name: {}, old_owner: {}, new_owner: {}", name, old_owner, new_owner); + } + } + } std::shared_ptr connection_; - std::unique_ptr dbus_interface_; + std::unique_ptr interface_; std::unique_ptr object_server_; - std::unordered_map> dbus_ids_to_monitor; // Alarm members and monitoring parties + std::unique_ptr name_lost_match_; + std::unordered_map> dbus_ids_to_monitor_; // Alarm members and monitoring parties }; } // namespace tfc::ipc_ruler diff --git a/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp b/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp index f876413ba7..4820d10bb9 100644 --- a/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp +++ b/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp @@ -103,4 +103,21 @@ static constexpr std::string_view make_match_rule() { return stx::string_view_join_v, interface, path, type>; } +/// \brief make complete dbus match rule +/// \tparam service_name service name +/// \tparam interface_name interface name +/// \tparam object_path object path +/// \tparam type type +/// \example tfc::dbus::match::rules::make_match_rule()), Reference: +/// https://dbus.freedesktop.org/doc/dbus-specification.html +template +static constexpr std::string_view make_match_rule() { + return stx::string_view_join_v, member, interface, path>; +} + } // namespace tfc::dbus::match::rules From 5e7acdcaf72d57ee971ebd6a4c2cb101cf424828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Thu, 20 Jun 2024 14:32:50 +0000 Subject: [PATCH 19/42] fill in the blanks of alarm dbus client --- exes/themis/inc/alarm_database.hpp | 16 ++-- exes/themis/inc/dbus_interface.hpp | 10 +-- .../inc/public/tfc/dbus/sdbusplus_meta.hpp | 4 + libs/snitch/inc/public/tfc/snitch.hpp | 19 ++-- libs/snitch/inc/public/tfc/snitch/common.hpp | 13 +-- .../public/tfc/snitch/details/dbus_client.hpp | 21 ++--- libs/snitch/src/dbus_client.cpp | 87 +++++++++++++++---- libs/snitch/tests/snitch_test.cpp | 11 ++- 8 files changed, 126 insertions(+), 55 deletions(-) diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index fa8bbe9058..8ddf6a9591 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -97,9 +97,9 @@ CREATE TABLE IF NOT EXISTS AlarmVariables( std::string_view description, std::string_view details, bool latching, - tfc::snitch::level_e alarm_level) -> std::uint64_t { + tfc::snitch::level_e alarm_level) -> snitch::api::alarm_id_t { std::string sha1_ascii = get_sha1(fmt::format("{}{}", description, details)); - std::uint64_t alarm_id = 0; + snitch::api::alarm_id_t alarm_id = 0; try { db_ << "BEGIN;"; db_ << fmt::format( @@ -110,7 +110,7 @@ CREATE TABLE IF NOT EXISTS AlarmVariables( if (insert_id < 0) { throw std::runtime_error("Failed to insert alarm into database"); } - alarm_id = static_cast(insert_id); + alarm_id = static_cast(insert_id); add_alarm_translation(alarm_id, "en", description, details); db_ << "COMMIT;"; } catch (std::exception& e) { @@ -139,7 +139,7 @@ LEFT JOIN AlarmTranslations ON Alarms.sha1sum = AlarmTranslations.sha1sum; )"; // Accept the second table paramters as unique ptr's as the values can be null, and we want to preserve that - db_ << query >> [&](std::uint64_t alarm_id, std::string tfc_id, std::string sha1sum, std::uint8_t alarm_level, + db_ << query >> [&](snitch::api::alarm_id_t alarm_id, std::string tfc_id, std::string sha1sum, std::uint8_t alarm_level, bool alarm_latching, std::optional locale, std::optional translated_description, std::optional translated_details) { auto iterator = alarms.find(alarm_id); @@ -161,7 +161,7 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; return alarm_list; } - auto add_alarm_translation(std::uint64_t alarm_id, + auto add_alarm_translation(snitch::api::alarm_id_t alarm_id, std::string_view locale, std::string_view description, std::string_view details) -> void { @@ -171,7 +171,7 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; db_ << query; } - auto set_alarm(std::uint64_t alarm_id, + auto set_alarm(snitch::api::alarm_id_t alarm_id, const std::unordered_map& variables, std::optional tp = {}) -> void { db_ << "BEGIN;"; @@ -193,7 +193,7 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; } db_ << "COMMIT;"; } - auto reset_alarm(std::uint64_t alarm_id, std::optional tp = {}) -> void { + auto reset_alarm(snitch::api::alarm_id_t alarm_id, std::optional tp = {}) -> void { db_ << fmt::format("INSERT INTO AlarmActivations(alarm_id, activation_time, activation_level) VALUES({},{},0)", alarm_id, milliseconds_since_epoch(tp)); } @@ -236,7 +236,7 @@ WHERE activation_time >= {} AND activation_time <= {})", std::vector activations; - db_ << populated_query >> [&](std::uint64_t activation_id, std::uint64_t alarm_id, std::int64_t activation_time, + db_ << populated_query >> [&](std::uint64_t activation_id, snitch::api::alarm_id_t alarm_id, std::int64_t activation_time, bool activation_level, std::optional primary_details, std::optional primary_description, std::optional backup_details, std::optional backup_description, std::optional tlocale, diff --git a/exes/themis/inc/dbus_interface.hpp b/exes/themis/inc/dbus_interface.hpp index 366aae9300..5306bf4943 100644 --- a/exes/themis/inc/dbus_interface.hpp +++ b/exes/themis/inc/dbus_interface.hpp @@ -25,7 +25,7 @@ class interface { explicit interface(boost::asio::io_context& ctx, tfc::themis::alarm_database& database) { connection_ = std::make_shared(ctx, tfc::dbus::sd_bus_open_system()); object_server_ = std::make_unique(connection_); - connection_->request_name(interface_name.data()); + connection_->request_name(service_name.data()); interface_ = object_server_->add_unique_interface(object_path.data(), interface_name.data()); @@ -35,21 +35,21 @@ class interface { }); interface_->register_method(std::string(methods::register_alarm), - [&](const sdbusplus::message_t& msg, std::string tfc_id, const std::string& description, const std::string& details, bool latching, int alarm_level) -> std::uint64_t { + [&](const sdbusplus::message_t& msg, std::string tfc_id, const std::string& description, const std::string& details, bool latching, std::underlying_type_t alarm_level) -> std::uint64_t { // TODO: User supplied alarm_level needs more verification return database.register_alarm_en(tfc_id, description, details, latching, static_cast(alarm_level)); }); interface_->register_method(std::string(methods::set_alarm), - [&](std::uint64_t alarm_id, const std::unordered_map& args) -> void { + [&](snitch::api::alarm_id_t alarm_id, const std::unordered_map& args) -> void { database.set_alarm(alarm_id, args); }); interface_->register_method(std::string(methods::reset_alarm), - [&](std::uint64_t alarm_id) -> void { + [&](snitch::api::alarm_id_t alarm_id) -> void { database.reset_alarm(alarm_id); }); interface_->register_method(std::string(methods::try_reset), - [&](std::uint64_t alarm_id) -> void { + [&](snitch::api::alarm_id_t alarm_id) -> void { auto message = interface_->new_signal(signals::try_reset.data()); message.append(alarm_id); message.signal_send(); diff --git a/libs/dbus_util/inc/public/tfc/dbus/sdbusplus_meta.hpp b/libs/dbus_util/inc/public/tfc/dbus/sdbusplus_meta.hpp index 2a933b068c..67062c064c 100644 --- a/libs/dbus_util/inc/public/tfc/dbus/sdbusplus_meta.hpp +++ b/libs/dbus_util/inc/public/tfc/dbus/sdbusplus_meta.hpp @@ -23,6 +23,10 @@ namespace sdbusplus::message::types::details { template struct type_id; +template <> +struct type_id : tuple_type_id +{}; + template struct type_id { static constexpr auto value{ type_id::value }; diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index 73d3299f98..c2c6dba546 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -36,11 +36,14 @@ class alarm { static constexpr auto keys_count{ std::max(description_arg_keys.size(), details_arg_keys.size()) }; /// \example alarm<{}, "short desc", "long desc"> warn(fmt::arg("key", "value"), fmt::arg("key2", 1)); - alarm(named_arg auto&& ... default_args) : default_values_{ std::make_pair(default_args.name, fmt::format("{}", default_args.value))... } { + /// \param unique_id A unique identifier for the alarm within this process + /// \param default_args Default fmt::arg values to populate the alarm with, e.g. fmt::arg("index", 1) for tank 1 etc. + alarm(std::shared_ptr conn, std::string_view unique_id, named_arg auto&& ... default_args) : default_values_{ std::make_pair(default_args.name, fmt::format("{}", default_args.value))... } { static_assert(detail::check_all_arguments_named(description), "All arguments must be named"); static_assert(detail::check_all_arguments_no_format(description), "All arguments may not have format specifiers"); [[maybe_unused]] static constexpr int num_args = sizeof...(default_args); static_assert(num_args <= keys_count, "Too many default arguments"); + } template @@ -58,20 +61,24 @@ class alarm { logger_.debug(fmt::vformat(details, store)); } private: + std::optional alarm_id_{}; + std::string my_name_; std::unordered_map default_values_; - logger::logger logger_{ "snitch" }; + std::shared_ptr conn_; + detail::dbus_client dbus_client_{ conn_ }; + logger::logger logger_{ fmt::format("snitch.{}", my_name_) }; }; template -using info = alarm<{ .requires_acknowledgement = false, .lvl = level_e::info }, description, details>; +using info = alarm; template -using warning = alarm<{ .requires_acknowledgement = false, .lvl = level_e::warning }, description, details>; +using warning = alarm; template -using warning_latched = alarm<{ .requires_acknowledgement = true, .lvl = level_e::warning }, description, details>; +using warning_latched = alarm; template using warning_ack = warning_latched; template -using error = alarm<{ .requires_acknowledgement = false, .lvl = level_e::error }, description, details>; +using error = alarm; } // namespace tfc::snitch diff --git a/libs/snitch/inc/public/tfc/snitch/common.hpp b/libs/snitch/inc/public/tfc/snitch/common.hpp index 8c7da15370..d9a9071958 100644 --- a/libs/snitch/inc/public/tfc/snitch/common.hpp +++ b/libs/snitch/inc/public/tfc/snitch/common.hpp @@ -6,7 +6,8 @@ namespace tfc::snitch { -enum struct level_e : std::int8_t { +// std::int8_t is not in the dbus spec, so we use std::int16_t instead +enum struct level_e : std::int16_t { all = -1, unknown, info, @@ -17,16 +18,17 @@ enum struct level_e : std::int8_t { namespace api { using time_point = std::chrono::time_point; -using alarm_id_t = std::int64_t; +using alarm_id_t = std::uint64_t; -enum struct active_e : std::int8_t { +// std::int8_t is not in the dbus spec, so we use std::int16_t instead +enum struct active_e : std::int16_t { all = -1, inactive = 0, active = 1 }; struct alarm { - std::uint64_t alarm_id; + alarm_id_t alarm_id; std::string tfc_id; std::string sha1sum; level_e lvl{ level_e::unknown }; @@ -40,7 +42,7 @@ struct alarm { }; struct activation { - std::uint64_t alarm_id; + alarm_id_t alarm_id; std::uint64_t activation_id; std::string description; std::string details; @@ -52,6 +54,7 @@ struct activation { bool in_requested_locale; }; namespace dbus { +static constexpr std::string_view service_name = "com.skaginn3x.Alarm"; static constexpr std::string_view interface_name = "com.skaginn3x.Alarm"; static constexpr std::string_view object_path = "/com/skaginn3x/Alarm"; namespace properties { diff --git a/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp index 98a53e7ba4..9bd7b0a2cd 100644 --- a/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp +++ b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -12,24 +13,24 @@ class dbus_client { public: explicit dbus_client(std::shared_ptr conn); - // todo completion tokens ... std function - auto register_alarm(std::string_view tfc_id, std::string_view description, std::string_view details, level_e lvl, bool ackable) -> api::alarm_id_t; + auto register_alarm(std::string_view tfc_id, std::string_view description, std::string_view details, bool resettable, level_e lvl, std::function token) -> void; - auto list_alarms() -> std::vector; + auto list_alarms(std::function)> token) -> void; - auto list_activations(std::string_view locale, std::uint64_t start_count, std::uint64_t count, level_e, api::active_e, api::time_point start, api::time_point end) -> std::vector; + auto list_activations(std::string_view locale, std::uint64_t start_count, std::uint64_t count, level_e, api::active_e, api::time_point start, api::time_point end, std::function)> token) -> void; - auto set_alarm(api::alarm_id_t, std::unordered_map args) -> void; + auto set_alarm(api::alarm_id_t, const std::unordered_map & args, std::function token = [](auto const&){}) -> void; - auto reset_alarm(api::alarm_id_t) -> void; + auto reset_alarm(api::alarm_id_t, std::function token = [](auto const&){}) -> void; - auto ack_alarm(api::alarm_id_t) -> void; + auto try_reset_alarm(api::alarm_id_t, std::function) -> void; - auto ack_all_alarms() -> void; - - auto match_ack_alarm(api::alarm_id_t, std::function callback) -> void; + auto try_reset_all_alarms(std::function) -> void; private: std::shared_ptr dbus_; + const std::string service_name_{ api::dbus::service_name }; + const std::string interface_name_{ api::dbus::interface_name }; + const std::string object_path_{ api::dbus::object_path }; }; } // namespace tfc::snitch::detail diff --git a/libs/snitch/src/dbus_client.cpp b/libs/snitch/src/dbus_client.cpp index d5df93d2dd..a4c9387a47 100644 --- a/libs/snitch/src/dbus_client.cpp +++ b/libs/snitch/src/dbus_client.cpp @@ -1,35 +1,84 @@ #include +#include +#include +#include #include namespace tfc::snitch::detail { -dbus_client::dbus_client(std::shared_ptr conn) : dbus_{ std::move(conn) } { +dbus_client::dbus_client(std::shared_ptr conn) : dbus_{ std::move(conn) } {} +auto dbus_client::register_alarm(std::string_view tfc_id, + std::string_view description, + std::string_view details, + bool resettable, + level_e lvl, + std::function token) -> void { + dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::register_alarm } + // arguments + , std::string{ tfc_id }, std::string{ description }, std::string{ details }, resettable, std::to_underlying(lvl)); } - -auto dbus_client::register_alarm(std::string_view tfc_id, std::string_view description, std::string_view details, level_e lvl, bool ackable) -> api::alarm_id_t { - return 0; -} - -auto dbus_client::list_alarms() -> std::vector { - return {}; +auto dbus_client::list_alarms(std::function)> token) -> void { + dbus_->async_method_call([token_mv = std::move(token)](std::error_code const& err, std::string buffer) { + if (err) { + std::invoke(token_mv, err, std::vector{}); + return; + } + std::vector result{}; + auto parse_err{ glz::read_json(result, buffer) }; + if (parse_err) { + // todo convert to error code + std::invoke(token_mv, std::make_error_code(std::errc::bad_message), std::vector{}); + return; + } + std::invoke(token_mv, err, result); + }, service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::list_alarms }); } - -auto dbus_client::list_activations(std::string_view locale, std::uint64_t start_count, std::uint64_t count, level_e, api::active_e, api::time_point start, api::time_point end) -> std::vector { - return {}; +auto dbus_client::list_activations(std::string_view locale, + std::uint64_t start_count, + std::uint64_t count, + level_e lvl, + api::active_e active, + api::time_point start, + api::time_point end, + std::function)> token) -> void { + dbus_->async_method_call([token_mv = std::move(token)](std::error_code const& err, std::string buffer) { + if (err) { + std::invoke(token_mv, err, std::vector{}); + return; + } + std::vector result{}; + auto parse_err{ glz::read_json(result, buffer) }; + if (parse_err) { + // todo convert to error code + std::invoke(token_mv, std::make_error_code(std::errc::bad_message), std::vector{}); + return; + } + std::invoke(token_mv, err, result); + }, service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::list_activations } + // arguments + , std::string{ locale }, start_count, count, std::to_underlying(lvl), std::to_underlying(active), start.time_since_epoch().count(), end.time_since_epoch().count() + ); } - -auto dbus_client::set_alarm(api::alarm_id_t, std::unordered_map args) -> void { +auto dbus_client::set_alarm(api::alarm_id_t id, const std::unordered_map & args, std::function token) + -> void { + dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::set_alarm } + // arguments + , id, args); } - -auto dbus_client::reset_alarm(api::alarm_id_t) -> void { +auto dbus_client::reset_alarm(api::alarm_id_t id, std::function token) -> void { + dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::reset_alarm } + // arguments + , id); } - -auto dbus_client::ack_alarm(api::alarm_id_t) -> void { +auto dbus_client::try_reset_alarm(api::alarm_id_t id, std::function token) -> void { + dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::try_reset } + // arguments + , id); } - -auto dbus_client::ack_all_alarms() -> void { +auto dbus_client::try_reset_all_alarms(std::function token) -> void { + dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::try_reset_all }); } } // namespace tfc::snitch::detail diff --git a/libs/snitch/tests/snitch_test.cpp b/libs/snitch/tests/snitch_test.cpp index 372c5577ff..6297a76b09 100644 --- a/libs/snitch/tests/snitch_test.cpp +++ b/libs/snitch/tests/snitch_test.cpp @@ -1,11 +1,16 @@ #include #include +#include +#include +#include + #include #include -#include +#include using std::string_view_literals::operator""sv; +namespace asio = boost::asio; namespace test { using tfc::snitch::detail::check_all_arguments_named; @@ -44,7 +49,9 @@ auto main(int argc, char** argv) -> int { tfc::base::init(argc, argv); "snitches"_test = [] { - tfc::snitch::info<"short desc {name}", "long desc {name} {index}"> tank(fmt::arg("name", "hello"), fmt::arg("index", 42)); + asio::io_context ctx; + auto connection = std::make_shared(ctx, tfc::dbus::sd_bus_open_system()); + tfc::snitch::info<"short desc {name}", "long desc {name} {index}"> tank(connection, "unique_id", fmt::arg("name", "hello"), fmt::arg("index", 42)); tank.set(); // warn.on_ack([]{}); From 18e00e7d2532c99791e1bfdae1f5a4fd9dc54915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Thu, 20 Jun 2024 15:21:07 +0000 Subject: [PATCH 20/42] alarm client wip --- libs/snitch/inc/public/tfc/snitch.hpp | 63 ++++++++++++++----- .../public/tfc/snitch/details/dbus_client.hpp | 7 +++ libs/snitch/src/dbus_client.cpp | 24 ++++++- 3 files changed, 77 insertions(+), 17 deletions(-) diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index c2c6dba546..d1326d9a50 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -16,7 +17,7 @@ namespace tfc::snitch { struct variance { - bool requires_acknowledgement{}; + bool resettable{}; level_e lvl{ level_e::unknown }; }; @@ -38,17 +39,35 @@ class alarm { /// \example alarm<{}, "short desc", "long desc"> warn(fmt::arg("key", "value"), fmt::arg("key2", 1)); /// \param unique_id A unique identifier for the alarm within this process /// \param default_args Default fmt::arg values to populate the alarm with, e.g. fmt::arg("index", 1) for tank 1 etc. - alarm(std::shared_ptr conn, std::string_view unique_id, named_arg auto&& ... default_args) : default_values_{ std::make_pair(default_args.name, fmt::format("{}", default_args.value))... } { + alarm(std::shared_ptr conn, std::string_view unique_id, named_arg auto&& ... default_args) : + conn_{ std::move(conn) }, + given_id_{ unique_id }, + default_values_{ std::make_pair(default_args.name, fmt::format("{}", default_args.value))... } { static_assert(detail::check_all_arguments_named(description), "All arguments must be named"); static_assert(detail::check_all_arguments_no_format(description), "All arguments may not have format specifiers"); [[maybe_unused]] static constexpr int num_args = sizeof...(default_args); static_assert(num_args <= keys_count, "Too many default arguments"); - + register_alarm(); } + alarm(alarm const&) = delete; + alarm& operator=(alarm const&) = delete; + alarm(alarm&&) = delete; + alarm& operator=(alarm&&) = delete; + ~alarm() = default; template - requires (var.requires_acknowledgement) - void on_ack(callback_t&& callback) { + requires (var.resettable) + void on_try_reset(callback_t&& callback) { + dbus_client_.on_try_reset_alarm([this, callback](api::alarm_id_t id) { + if (active_ && id == alarm_id_) { + std::invoke(callback); + } + }); + dbus_client_.on_try_reset_all_alarms([this, callback] { + if (active_) { + std::invoke(callback); + } + }); } void set(named_arg auto&& ... args) { @@ -59,26 +78,42 @@ class alarm { (store.push_back(args), ...); logger_.debug(fmt::vformat(description, store)); logger_.debug(fmt::vformat(details, store)); + + std::unordered_map params; + // for (auto const& arg : store) { + // params[key] = fmt::format("{}", value); + // } } private: - std::optional alarm_id_{}; - std::string my_name_; - std::unordered_map default_values_; + void register_alarm() { + dbus_client_.register_alarm(tfc_id_, description, details, var.resettable, var.lvl, [this](std::error_code const& ec, api::alarm_id_t id) { + if (ec) { + logger_.error("Failed to register alarm: {}", ec.message()); + return; + } + alarm_id_ = id; + }); + } + bool active_{}; std::shared_ptr conn_; + std::string given_id_; + std::string tfc_id_{ fmt::format("{}.{}.{}", base::get_exe_name(), base::get_proc_name(), given_id_) }; + std::unordered_map default_values_; + std::optional alarm_id_{}; detail::dbus_client dbus_client_{ conn_ }; - logger::logger logger_{ fmt::format("snitch.{}", my_name_) }; + logger::logger logger_{ fmt::format("snitch.{}", given_id_) }; }; template -using info = alarm; +using info = alarm; template -using warning = alarm; +using warning = alarm; template -using warning_latched = alarm; +using warning_latched = alarm; template -using warning_ack = warning_latched; +using warning_resettable = warning_latched; template -using error = alarm; +using error = alarm; } // namespace tfc::snitch diff --git a/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp index 9bd7b0a2cd..7af144827e 100644 --- a/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp +++ b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -26,8 +27,14 @@ class dbus_client { auto try_reset_alarm(api::alarm_id_t, std::function) -> void; auto try_reset_all_alarms(std::function) -> void; + + auto on_try_reset_alarm(std::function) -> void; + + auto on_try_reset_all_alarms(std::function) -> void; private: std::shared_ptr dbus_; + std::unique_ptr> try_reset_; + std::unique_ptr> try_reset_all_; const std::string service_name_{ api::dbus::service_name }; const std::string interface_name_{ api::dbus::interface_name }; const std::string object_path_{ api::dbus::object_path }; diff --git a/libs/snitch/src/dbus_client.cpp b/libs/snitch/src/dbus_client.cpp index a4c9387a47..debcc3173d 100644 --- a/libs/snitch/src/dbus_client.cpp +++ b/libs/snitch/src/dbus_client.cpp @@ -1,13 +1,18 @@ -#include #include +#include +#include -#include +#include #include #include +#include namespace tfc::snitch::detail { +static constexpr auto try_reset_match_{ dbus::match::rules::make_match_rule() }; +static constexpr auto try_reset_all_match_{ dbus::match::rules::make_match_rule() }; + dbus_client::dbus_client(std::shared_ptr conn) : dbus_{ std::move(conn) } {} auto dbus_client::register_alarm(std::string_view tfc_id, std::string_view description, @@ -78,7 +83,20 @@ auto dbus_client::try_reset_alarm(api::alarm_id_t id, std::function token) -> void { - dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::try_reset_all }); + dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, + std::string{ api::dbus::methods::try_reset_all }); +} +auto dbus_client::on_try_reset_alarm(std::function token) -> void { + try_reset_ = std::make_unique(*dbus_, try_reset_match_.data(), [token_mv = std::move(token)](sdbusplus::message_t& msg) { + api::alarm_id_t id; + msg.read(id); + std::invoke(token_mv, id); + }); +} +auto dbus_client::on_try_reset_all_alarms(std::function token) -> void { + try_reset_all_ = std::make_unique(*dbus_, try_reset_all_match_.data(), [token_mv = std::move(token)](sdbusplus::message_t&) { + std::invoke(token_mv); + }); } } // namespace tfc::snitch::detail From 1fb07689151b01255fb403e3194d17b42029fdad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Thu, 20 Jun 2024 15:26:16 +0000 Subject: [PATCH 21/42] Modify the reset behaviour to work on the same activation, Dont know what I was thinking with the other row --- exes/themis/inc/alarm_database.hpp | 27 ++++++++++++++-------- exes/themis/tests/themis_database_test.cpp | 8 +++---- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index 8ddf6a9591..0488d5803f 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -47,6 +47,7 @@ CREATE TABLE IF NOT EXISTS Alarms( sha1sum TEXT NOT NULL, alarm_level INTEGER NOT NULL, alarm_latching BOOLEAN NOT NULL, + inserted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(tfc_id, sha1sum) ON CONFLICT IGNORE ); )"; @@ -57,6 +58,7 @@ CREATE TABLE IF NOT EXISTS AlarmTranslations( locale TEXT NOT NULL, details TEXT NOT NULL, description TEXT NOT NULL, + inserted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(sha1sum, locale) ON CONFLICT REPLACE FOREIGN KEY(alarm_id) REFERENCES Alarms(alarm_id) ); @@ -71,7 +73,9 @@ CREATE TABLE IF NOT EXISTS AlarmActivations( activation_id INTEGER PRIMARY KEY, alarm_id INTEGER NOT NULL, activation_time LONG INTEGER NOT NULL, + reset_time LONG INTEGER, activation_level BOOLEAN NOT NULL, + inserted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(alarm_id) REFERENCES Alarms(alarm_id) ); )"; @@ -171,31 +175,36 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; db_ << query; } + /** + * @brief Set an alarm in the database + * @param alarm_id the id of the alarm + * @param variables the variables for this activation + * @param tp an optional timepoint + * @return the activation_id + */ auto set_alarm(snitch::api::alarm_id_t alarm_id, const std::unordered_map& variables, - std::optional tp = {}) -> void { + std::optional tp = {}) -> std::uint64_t { db_ << "BEGIN;"; + std::uint64_t activation_id; try { db_ << fmt::format("INSERT INTO AlarmActivations(alarm_id, activation_time, activation_level) VALUES({},{},1)", alarm_id, milliseconds_since_epoch(tp)); - std::int64_t last_insert_rowid = db_.last_insert_rowid(); - if (last_insert_rowid < 0) { - throw std::runtime_error("Failed to insert activation into database"); - } + activation_id = static_cast(db_.last_insert_rowid()); for (auto& [key, value] : variables) { db_ << fmt::format("INSERT INTO AlarmVariables(activation_id, variable_key, variable_value) VALUES({},'{}','{}');", - last_insert_rowid, key, value); + activation_id, key, value); } } catch (std::exception& e) { db_ << "ROLLBACK;"; throw e; } db_ << "COMMIT;"; + return activation_id; } - auto reset_alarm(snitch::api::alarm_id_t alarm_id, std::optional tp = {}) -> void { - db_ << fmt::format("INSERT INTO AlarmActivations(alarm_id, activation_time, activation_level) VALUES({},{},0)", alarm_id, - milliseconds_since_epoch(tp)); + auto reset_alarm(snitch::api::alarm_id_t activation_id, std::optional tp = {}) -> void { + db_ << fmt::format("UPDATE AlarmActivations SET activation_level = 0, reset_time = {} WHERE activation_id = {};", milliseconds_since_epoch(tp), activation_id); } [[nodiscard]] auto list_activations(std::string_view locale, diff --git a/exes/themis/tests/themis_database_test.cpp b/exes/themis/tests/themis_database_test.cpp index ab40163613..a358659540 100644 --- a/exes/themis/tests/themis_database_test.cpp +++ b/exes/themis/tests/themis_database_test.cpp @@ -93,8 +93,8 @@ auto main(int argc, char** argv) -> int { // Add some activations for(int i = 0; i < 10; i++){ - alarm_db.set_alarm(insert_id, {}); - alarm_db.reset_alarm(insert_id); + auto activation_id = alarm_db.set_alarm(insert_id, {}); + alarm_db.reset_alarm(activation_id); } // Insert an invalid activation expect(throws([&]{alarm_db.set_alarm(999, {}); })); @@ -104,12 +104,12 @@ auto main(int argc, char** argv) -> int { "es", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::all, tfc::themis::alarm_database::timepoint_from_milliseconds(0), tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); - expect(activations.size() == 20) << activations.size(); + expect(activations.size() == 10) << activations.size(); activations = alarm_db.list_activations( "es", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, tfc::themis::alarm_database::timepoint_from_milliseconds(0), tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); - expect(activations.size() == 10) << activations.size(); + expect(activations.size() == 0) << activations.size(); activations = alarm_db.list_activations( "es", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::inactive, tfc::themis::alarm_database::timepoint_from_milliseconds(0), From 94c5bebe3d432d4d43692c938b708e1a0ee9c3fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Thu, 20 Jun 2024 15:54:52 +0000 Subject: [PATCH 22/42] Added some correctness checks --- exes/themis/inc/alarm_database.hpp | 52 ++++++++++++++++---- exes/themis/inc/dbus_interface.hpp | 4 +- exes/themis/tests/themis_database_test.cpp | 49 ++++++++++++++++-- libs/snitch/inc/public/tfc/snitch/common.hpp | 3 +- 4 files changed, 92 insertions(+), 16 deletions(-) diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index 0488d5803f..daf0dc831f 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -8,12 +8,12 @@ #include #include #include +#include #include #include #include #include #include -#include namespace tfc::themis { @@ -170,11 +170,32 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; std::string_view description, std::string_view details) -> void { auto query = fmt::format( - "INSERT INTO AlarmTranslations(sha1sum, alarm_id, locale, description, details) SELECT DISTINCT sha1sum, {}, '{}','{}','{}' FROM Alarms where alarm_id = {}", + "INSERT INTO AlarmTranslations(sha1sum, alarm_id, locale, description, details) SELECT DISTINCT sha1sum, {}, " + "'{}','{}','{}' FROM Alarms where alarm_id = {}", alarm_id, locale, description, details, alarm_id); db_ << query; } + [[nodiscard]] auto is_alarm_active(snitch::api::alarm_id_t alarm_id) -> bool { + std::int64_t count = 0; + db_ << fmt::format("SELECT COUNT(*) FROM AlarmActivations WHERE alarm_id = {} AND activation_level = 1;", alarm_id) >> + [&](std::int64_t c) { count = c; }; + return count > 0; + } + + [[nodiscard]] auto is_activation_high(snitch::api::alarm_id_t activation_id) -> bool { + bool active = false; + db_ << fmt::format("SELECT activation_level FROM AlarmActivations WHERE activation_id = {} AND activation_level = 1;", activation_id) >> + [&](bool a) { active = a;}; + return active; + } + + [[nodiscard]] auto active_alarm_count() -> std::uint64_t { + std::uint64_t count = 0; + db_ << "SELECT COUNT(*) FROM AlarmActivations WHERE activation_level = 1;" >> [&](std::uint64_t c) { count = c; }; + return count; + } + /** * @brief Set an alarm in the database * @param alarm_id the id of the alarm @@ -182,9 +203,12 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; * @param tp an optional timepoint * @return the activation_id */ - auto set_alarm(snitch::api::alarm_id_t alarm_id, + [[nodiscard]] auto set_alarm(snitch::api::alarm_id_t alarm_id, const std::unordered_map& variables, std::optional tp = {}) -> std::uint64_t { + if (is_alarm_active(alarm_id)){ + throw std::runtime_error("Alarm is already active"); + } db_ << "BEGIN;"; std::uint64_t activation_id; try { @@ -204,7 +228,11 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; return activation_id; } auto reset_alarm(snitch::api::alarm_id_t activation_id, std::optional tp = {}) -> void { - db_ << fmt::format("UPDATE AlarmActivations SET activation_level = 0, reset_time = {} WHERE activation_id = {};", milliseconds_since_epoch(tp), activation_id); + if (!is_activation_high(activation_id)){ + throw std::runtime_error("Cannot reset an inactive activation"); + } + db_ << fmt::format("UPDATE AlarmActivations SET activation_level = 0, reset_time = {} WHERE activation_id = {};", + milliseconds_since_epoch(tp), activation_id); } [[nodiscard]] auto list_activations(std::string_view locale, @@ -219,6 +247,7 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; activation_id, Alarms.alarm_id, activation_time, + reset_time, activation_level, primary_text.details, primary_text.description, @@ -245,7 +274,8 @@ WHERE activation_time >= {} AND activation_time <= {})", std::vector activations; - db_ << populated_query >> [&](std::uint64_t activation_id, snitch::api::alarm_id_t alarm_id, std::int64_t activation_time, + db_ << populated_query >> [&](std::uint64_t activation_id, snitch::api::alarm_id_t alarm_id, + std::int64_t activation_time, std::optional reset_time, bool activation_level, std::optional primary_details, std::optional primary_description, std::optional backup_details, std::optional backup_description, std::optional tlocale, @@ -256,11 +286,15 @@ WHERE activation_time >= {} AND activation_time <= {})", std::string details = primary_details.value_or(backup_details.value()); std::string description = primary_description.value_or(backup_description.value()); bool in_locale = primary_description.has_value() && primary_details.has_value(); - activations.emplace_back(alarm_id, activation_id, description, details, tlocale.value(), - activation_level, static_cast(alarm_level), alarm_latching, - timepoint_from_milliseconds(activation_time), in_locale); + std::optional final_reset_time = std::nullopt; + if (reset_time.has_value()) { + final_reset_time = timepoint_from_milliseconds(reset_time.value()); + } + activations.emplace_back(alarm_id, activation_id, description, details, tlocale.value(), activation_level, + static_cast(alarm_level), alarm_latching, + timepoint_from_milliseconds(activation_time), final_reset_time, in_locale); }; - for(auto& activation : activations) { + for (auto& activation : activations) { // TODO: This is not great would be better to do this in a large query // As of now we are doing a query for each activation to get the variables std::vector> variables; diff --git a/exes/themis/inc/dbus_interface.hpp b/exes/themis/inc/dbus_interface.hpp index 5306bf4943..19a1423e56 100644 --- a/exes/themis/inc/dbus_interface.hpp +++ b/exes/themis/inc/dbus_interface.hpp @@ -41,8 +41,8 @@ class interface { }); interface_->register_method(std::string(methods::set_alarm), - [&](snitch::api::alarm_id_t alarm_id, const std::unordered_map& args) -> void { - database.set_alarm(alarm_id, args); + [&](snitch::api::alarm_id_t alarm_id, const std::unordered_map& args) -> std::uint64_t { + return database.set_alarm(alarm_id, args); }); interface_->register_method(std::string(methods::reset_alarm), [&](snitch::api::alarm_id_t alarm_id) -> void { diff --git a/exes/themis/tests/themis_database_test.cpp b/exes/themis/tests/themis_database_test.cpp index a358659540..c2ae986903 100644 --- a/exes/themis/tests/themis_database_test.cpp +++ b/exes/themis/tests/themis_database_test.cpp @@ -97,7 +97,7 @@ auto main(int argc, char** argv) -> int { alarm_db.reset_alarm(activation_id); } // Insert an invalid activation - expect(throws([&]{alarm_db.set_alarm(999, {}); })); + expect(throws([&]{[[maybe_unused]] auto _ = alarm_db.set_alarm(999, {}); })); // Verify our inserts activations = alarm_db.list_activations( @@ -121,7 +121,7 @@ auto main(int argc, char** argv) -> int { tfc::themis::alarm_database alarm_db(true); auto insert_id = alarm_db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); alarm_db.add_alarm_translation(alarm_db.list_alarms().at(0).alarm_id, "es", "spanish description", "spanish details"); - alarm_db.set_alarm(insert_id, {}); + [[maybe_unused]] auto _ = alarm_db.set_alarm(insert_id, {}); auto activations = alarm_db.list_activations( "is", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, tfc::themis::alarm_database::timepoint_from_milliseconds(0), @@ -145,7 +145,7 @@ auto main(int argc, char** argv) -> int { // Add a variable alarm auto var_alarm_id = alarm_db.register_alarm_en("tfc_id", "{var}", "{var}", false, tfc::snitch::level_e::info); expect(alarm_db.list_alarms().size() == 1); - alarm_db.set_alarm(var_alarm_id, {{ "var", "10.0"}}); + [[maybe_unused]] auto _ = alarm_db.set_alarm(var_alarm_id, {{ "var", "10.0"}}); auto activations = alarm_db.list_activations( "en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, tfc::themis::alarm_database::timepoint_from_milliseconds(0), @@ -158,7 +158,7 @@ auto main(int argc, char** argv) -> int { var_alarm_id = alarm_db.register_alarm_en("tfc_id", "description {var} {var2} {var3}", "details {var} {var2} {var3}", false, tfc::snitch::level_e::warning); expect(alarm_db.list_alarms().size() == 2); alarm_db.add_alarm_translation(var_alarm_id, "es", "spanish description {var} {var2} {var3}", "spanish details {var} {var2} {var3}"); - alarm_db.set_alarm(var_alarm_id, {{ "var", "10.0"}, {"var2", "20.0"}, {"var3", "30.0"}}); + _ = alarm_db.set_alarm(var_alarm_id, {{ "var", "10.0"}, {"var2", "20.0"}, {"var3", "30.0"}}); activations = alarm_db.list_activations( "en", 0, 10000, tfc::snitch::level_e::warning, tfc::snitch::api::active_e::active, tfc::themis::alarm_database::timepoint_from_milliseconds(0), @@ -184,4 +184,45 @@ auto main(int argc, char** argv) -> int { expect(activations.at(0).description == "description 10.0 20.0 30.0") << activations.at(0).description; expect(activations.at(0).details == "details 10.0 20.0 30.0") << activations.at(0).details; }; + "Test the millisecond time storage"_test = []{ + auto min_time = alarm_database::timepoint_from_milliseconds(std::numeric_limits::min()); + auto max_time = alarm_database::timepoint_from_milliseconds(std::numeric_limits::max()); + auto time = alarm_database::timepoint_from_milliseconds(0); + expect(time.time_since_epoch().count() == 0); + time = alarm_database::timepoint_from_milliseconds(1000); + expect(time.time_since_epoch().count() == 1000); + time = alarm_database::timepoint_from_milliseconds(1000000); + expect(time.time_since_epoch().count() == 1000000); + time = alarm_database::timepoint_from_milliseconds(1000000000); + expect(time.time_since_epoch().count() == 1000000000); + time = alarm_database::timepoint_from_milliseconds(1000000000000); + expect(time.time_since_epoch().count() == 1000000000000); + time = alarm_database::timepoint_from_milliseconds(1000000000000000); + expect(time.time_since_epoch().count() == 1000000000000000); + + tfc::themis::alarm_database db(true); + auto alarm_id = db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); + time = alarm_database::timepoint_from_milliseconds(1); + auto activation_id = db.set_alarm(alarm_id, {}, time); + auto activations = db.list_activations("en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::all, min_time, max_time); + expect(activations.size() == 1); + expect(activations.at(0).set_timestamp == time); + expect(!activations.at(0).reset_timestamp.has_value()); + auto reset_time = alarm_database::timepoint_from_milliseconds(2); + db.reset_alarm(activation_id, reset_time); + activations = db.list_activations("en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::all, min_time, max_time); + expect(!db.is_alarm_active(alarm_id)); + expect(activations.size() == 1); + expect(activations.at(0).set_timestamp == time); + expect(activations.at(0).reset_timestamp.has_value()); + expect(activations.at(0).reset_timestamp == reset_time); + }; + "You should not be able to set an already set alarm"_test = []{ + tfc::themis::alarm_database db(true); + auto alarm_id = db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); + auto activation_id = db.set_alarm(alarm_id, {}); + expect(throws([&]{[[maybe_unused]] auto _ = db.set_alarm(alarm_id, {}); })); + db.reset_alarm(activation_id); + expect(throws([&]{ db.reset_alarm(activation_id, {}); })); + }; } diff --git a/libs/snitch/inc/public/tfc/snitch/common.hpp b/libs/snitch/inc/public/tfc/snitch/common.hpp index d9a9071958..d2ee01af5d 100644 --- a/libs/snitch/inc/public/tfc/snitch/common.hpp +++ b/libs/snitch/inc/public/tfc/snitch/common.hpp @@ -50,7 +50,8 @@ struct activation { bool active{}; level_e lvl{ level_e::unknown }; bool latching{}; - time_point timestamp; + time_point set_timestamp; + std::optional reset_timestamp; bool in_requested_locale; }; namespace dbus { From 0c5c7188f1c26d38bcab40468c044a8cc2a693bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Fri, 21 Jun 2024 09:12:23 +0000 Subject: [PATCH 23/42] implement set and reset for snitch --- libs/snitch/inc/public/tfc/snitch.hpp | 97 ++++++++++++++----- .../public/tfc/snitch/format_extension.hpp | 1 + 2 files changed, 75 insertions(+), 23 deletions(-) diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index d1326d9a50..ca78069cea 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -1,21 +1,26 @@ #pragma once +#include #include #include +#include -#include #include +#include +#include -#include #include -#include -#include +#include #include -#include #include +#include +#include +#include namespace tfc::snitch { +namespace asio = boost::asio; + struct variance { bool resettable{}; level_e lvl{ level_e::unknown }; @@ -39,10 +44,9 @@ class alarm { /// \example alarm<{}, "short desc", "long desc"> warn(fmt::arg("key", "value"), fmt::arg("key2", 1)); /// \param unique_id A unique identifier for the alarm within this process /// \param default_args Default fmt::arg values to populate the alarm with, e.g. fmt::arg("index", 1) for tank 1 etc. - alarm(std::shared_ptr conn, std::string_view unique_id, named_arg auto&& ... default_args) : - conn_{ std::move(conn) }, - given_id_{ unique_id }, - default_values_{ std::make_pair(default_args.name, fmt::format("{}", default_args.value))... } { + alarm(std::shared_ptr conn, std::string_view unique_id, named_arg auto&&... default_args) + : conn_{ std::move(conn) }, given_id_{ unique_id }, + default_values_{ std::make_pair(default_args.name, fmt::format("{}", default_args.value))... } { static_assert(detail::check_all_arguments_named(description), "All arguments must be named"); static_assert(detail::check_all_arguments_no_format(description), "All arguments may not have format specifiers"); [[maybe_unused]] static constexpr int num_args = sizeof...(default_args); @@ -56,7 +60,7 @@ class alarm { ~alarm() = default; template - requires (var.resettable) + requires(var.resettable) void on_try_reset(callback_t&& callback) { dbus_client_.on_try_reset_alarm([this, callback](api::alarm_id_t id) { if (active_ && id == alarm_id_) { @@ -70,7 +74,7 @@ class alarm { }); } - void set(named_arg auto&& ... args) { + void set(named_arg auto&&... args) { fmt::dynamic_format_arg_store store; for (auto const& [key, value] : default_values_) { store.push_back(fmt::arg(key.c_str(), value)); @@ -79,31 +83,79 @@ class alarm { logger_.debug(fmt::vformat(description, store)); logger_.debug(fmt::vformat(details, store)); - std::unordered_map params; - // for (auto const& arg : store) { - // params[key] = fmt::format("{}", value); - // } + auto params{ default_values_ }; + params.emplace(std::make_pair(args.name, fmt::format("{}", args.value))...); + + if (active_) { + logger_.info("alarm already active, not doing anything"); + } + + active_ = true; + + if (alarm_id_) { + dbus_client_.set_alarm(alarm_id_.value(), params, [this](std::error_code const& ec) { + if (ec) { + logger_.error("Failed to set alarm: {}", ec.message()); + // I am both inclined to reset active flag here aswell as not ... I think I will not + // active_ = false; + } + }); + } else { + retry_timer_.expires_after(std::chrono::seconds(1)); + retry_timer_.async_wait([this, args...](std::error_code const& ec) { + if (ec) { + logger_.error("Retry set timer failed: {}", ec.message()); + return; + } + logger_.debug("Retrying set alarm"); + set(args...); + }); + } } -private: - void register_alarm() { - dbus_client_.register_alarm(tfc_id_, description, details, var.resettable, var.lvl, [this](std::error_code const& ec, api::alarm_id_t id) { + + void reset() { + if (!active_) { + logger_.info("alarm already inactive, not doing anything"); + return; + } + active_ = false; + if (!alarm_id_) { + logger_.info("NO alarm ID, forget that alarm ever happened, the timings would be off so would not be valuable information"); + return; + } + dbus_client_.reset_alarm(alarm_id_.value(), [this](std::error_code const& ec) { if (ec) { - logger_.error("Failed to register alarm: {}", ec.message()); - return; + logger_.error("Failed to reset alarm: {}", ec.message()); + // I am both inclined to set active flag here aswell as not ... I think I will not + // active_ = true; } - alarm_id_ = id; }); } + +private: + void register_alarm() { + dbus_client_.register_alarm(tfc_id_, description, details, var.resettable, var.lvl, + [this](std::error_code const& ec, api::alarm_id_t id) { + if (ec) { + logger_.error("Failed to register alarm: {}", ec.message()); + return; + } + alarm_id_ = id; + }); + } bool active_{}; std::shared_ptr conn_; std::string given_id_; std::string tfc_id_{ fmt::format("{}.{}.{}", base::get_exe_name(), base::get_proc_name(), given_id_) }; std::unordered_map default_values_; std::optional alarm_id_{}; + asio::steady_timer retry_timer_{ conn_->get_io_context() }; detail::dbus_client dbus_client_{ conn_ }; logger::logger logger_{ fmt::format("snitch.{}", given_id_) }; }; + + template using info = alarm; template @@ -115,5 +167,4 @@ using warning_resettable = warning_latched; template using error = alarm; - -} // namespace tfc::snitch +} // namespace tfc::snitch diff --git a/libs/snitch/inc/public/tfc/snitch/format_extension.hpp b/libs/snitch/inc/public/tfc/snitch/format_extension.hpp index 8bccc7f297..b8dcd9dbaa 100644 --- a/libs/snitch/inc/public/tfc/snitch/format_extension.hpp +++ b/libs/snitch/inc/public/tfc/snitch/format_extension.hpp @@ -2,6 +2,7 @@ #include #include +#include #include From 058f6806fe0c579c242db9f5a88cd809a44d8e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Fri, 21 Jun 2024 10:43:55 +0000 Subject: [PATCH 24/42] Updated policy and added tests --- exes/themis/inc/alarm_database.hpp | 51 +++++++++++++------- exes/themis/tests/themis_database_test.cpp | 31 ++++++++++++ libs/snitch/inc/public/tfc/snitch/common.hpp | 1 + 3 files changed, 66 insertions(+), 17 deletions(-) diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index daf0dc831f..f40088319f 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -48,9 +48,9 @@ CREATE TABLE IF NOT EXISTS Alarms( alarm_level INTEGER NOT NULL, alarm_latching BOOLEAN NOT NULL, inserted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + registered_at LONG INTEGER NOT NULL, UNIQUE(tfc_id, sha1sum) ON CONFLICT IGNORE -); - )"; +);)"; db_ << R"( CREATE TABLE IF NOT EXISTS AlarmTranslations( sha1sum TEXT NOT NULL, @@ -101,21 +101,29 @@ CREATE TABLE IF NOT EXISTS AlarmVariables( std::string_view description, std::string_view details, bool latching, - tfc::snitch::level_e alarm_level) -> snitch::api::alarm_id_t { + tfc::snitch::level_e alarm_level, + std::optional registered_at = std::nullopt) -> snitch::api::alarm_id_t { std::string sha1_ascii = get_sha1(fmt::format("{}{}", description, details)); snitch::api::alarm_id_t alarm_id = 0; + auto ms_count_registered_at = milliseconds_since_epoch(registered_at); try { db_ << "BEGIN;"; db_ << fmt::format( - "INSERT INTO Alarms(tfc_id, sha1sum, alarm_level, alarm_latching) " - "VALUES('{}','{}',{},{});", - tfc_id, sha1_ascii, static_cast(alarm_level), latching ? 1 : 0); + "INSERT INTO Alarms(tfc_id, sha1sum, alarm_level, alarm_latching, registered_at) VALUES('{}','{}',{}, {}, {}) ON " + "CONFLICT (tfc_id, sha1sum) DO UPDATE SET registered_at={};", + tfc_id, sha1_ascii, static_cast(alarm_level), latching ? 1 : 0, ms_count_registered_at, + ms_count_registered_at); auto insert_id = db_.last_insert_rowid(); if (insert_id < 0) { throw std::runtime_error("Failed to insert alarm into database"); } alarm_id = static_cast(insert_id); add_alarm_translation(alarm_id, "en", description, details); + + // Reset the alarm if high on register + if (is_alarm_active(alarm_id)){ + reset_alarm(alarm_id); + } db_ << "COMMIT;"; } catch (std::exception& e) { // Rollback the transaction and rethrow @@ -135,6 +143,7 @@ SELECT Alarms.sha1sum, alarm_level, Alarms.alarm_latching, + Alarms.registered_at, AlarmTranslations.locale, AlarmTranslations.description, AlarmTranslations.details @@ -144,14 +153,21 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; )"; // Accept the second table paramters as unique ptr's as the values can be null, and we want to preserve that db_ << query >> [&](snitch::api::alarm_id_t alarm_id, std::string tfc_id, std::string sha1sum, std::uint8_t alarm_level, - bool alarm_latching, std::optional locale, + bool alarm_latching, std::optional registered_at, std::optional locale, std::optional translated_description, std::optional translated_details) { auto iterator = alarms.find(alarm_id); if (iterator == alarms.end()) { - alarms.insert( - { alarm_id, - tfc::snitch::api::alarm{ - alarm_id, tfc_id, sha1sum, static_cast(alarm_level), alarm_latching, {} } }); + std::optional final_registered_at = std::nullopt; + if (registered_at.has_value()) { + final_registered_at = timepoint_from_milliseconds(registered_at.value()); + } + alarms.insert({ alarm_id, tfc::snitch::api::alarm{ alarm_id, + tfc_id, + sha1sum, + static_cast(alarm_level), + alarm_latching, + final_registered_at, + {} } }); } if (locale.has_value() && translated_details.has_value() && translated_description.has_value()) { alarms[alarm_id].translations.insert( @@ -185,8 +201,9 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; [[nodiscard]] auto is_activation_high(snitch::api::alarm_id_t activation_id) -> bool { bool active = false; - db_ << fmt::format("SELECT activation_level FROM AlarmActivations WHERE activation_id = {} AND activation_level = 1;", activation_id) >> - [&](bool a) { active = a;}; + db_ << fmt::format("SELECT activation_level FROM AlarmActivations WHERE activation_id = {} AND activation_level = 1;", + activation_id) >> + [&](bool a) { active = a; }; return active; } @@ -204,9 +221,9 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; * @return the activation_id */ [[nodiscard]] auto set_alarm(snitch::api::alarm_id_t alarm_id, - const std::unordered_map& variables, - std::optional tp = {}) -> std::uint64_t { - if (is_alarm_active(alarm_id)){ + const std::unordered_map& variables, + std::optional tp = {}) -> std::uint64_t { + if (is_alarm_active(alarm_id)) { throw std::runtime_error("Alarm is already active"); } db_ << "BEGIN;"; @@ -228,7 +245,7 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; return activation_id; } auto reset_alarm(snitch::api::alarm_id_t activation_id, std::optional tp = {}) -> void { - if (!is_activation_high(activation_id)){ + if (!is_activation_high(activation_id)) { throw std::runtime_error("Cannot reset an inactive activation"); } db_ << fmt::format("UPDATE AlarmActivations SET activation_level = 0, reset_time = {} WHERE activation_id = {};", diff --git a/exes/themis/tests/themis_database_test.cpp b/exes/themis/tests/themis_database_test.cpp index c2ae986903..1fa25f2b75 100644 --- a/exes/themis/tests/themis_database_test.cpp +++ b/exes/themis/tests/themis_database_test.cpp @@ -225,4 +225,35 @@ auto main(int argc, char** argv) -> int { db.reset_alarm(activation_id); expect(throws([&]{ db.reset_alarm(activation_id, {}); })); }; + "Verify alarm id when replacing reinstantiating alarms"_test = []{ + tfc::themis::alarm_database db(true); + auto alarm_id = db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); + auto new_alarm_id = db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); + auto not_the_same = db.register_alarm_en("tfc_id2", "description", "details", false, tfc::snitch::level_e::info); + expect(alarm_id == new_alarm_id); + expect(alarm_id != not_the_same); + }; + "re-registering the same alarm should update registered_at"_test = []{ + tfc::themis::alarm_database db(true); + auto tp = tfc::themis::alarm_database::timepoint_from_milliseconds(0); + [[maybe_unused]] auto alarm_id = db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info, tp); + auto alarms = db.list_alarms(); + expect(alarms.size() == 1); + expect(alarms.at(0).registered_at == tp); + + tp = tfc::themis::alarm_database::timepoint_from_milliseconds(1); + alarm_id = db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info, tp); + alarms = db.list_alarms(); + expect(alarms.size() == 1); + expect(alarms.at(0).registered_at == tp); + }; + "registering an alarm should result in a clean-slate"_test = []{ + auto db = tfc::themis::alarm_database(true); + auto alarm_id = db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); + [[maybe_unused]] auto activation_id = db.set_alarm(alarm_id, {}); + expect(db.is_alarm_active(alarm_id)); + auto new_alarm_id = db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); + expect(alarm_id == new_alarm_id); + expect(!db.is_alarm_active(alarm_id)); + }; } diff --git a/libs/snitch/inc/public/tfc/snitch/common.hpp b/libs/snitch/inc/public/tfc/snitch/common.hpp index d2ee01af5d..37e7019f47 100644 --- a/libs/snitch/inc/public/tfc/snitch/common.hpp +++ b/libs/snitch/inc/public/tfc/snitch/common.hpp @@ -38,6 +38,7 @@ struct alarm { std::string details; }; using locale = std::string; + std::optional registered_at; std::unordered_map translations; }; From d0ab38597169cff68d35fed51bff28b5b8e0cbdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Fri, 21 Jun 2024 11:21:19 +0000 Subject: [PATCH 25/42] move implementation to source file --- libs/snitch/CMakeLists.txt | 1 + libs/snitch/inc/public/tfc/snitch.hpp | 95 ++-------------- libs/snitch/inc/public/tfc/snitch/common.hpp | 1 + .../public/tfc/snitch/details/dbus_client.hpp | 4 +- .../public/tfc/snitch/details/snitch_impl.hpp | 55 ++++++++++ libs/snitch/src/dbus_client.cpp | 4 +- libs/snitch/src/snitch_impl.cpp | 103 ++++++++++++++++++ 7 files changed, 174 insertions(+), 89 deletions(-) create mode 100644 libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp create mode 100644 libs/snitch/src/snitch_impl.cpp diff --git a/libs/snitch/CMakeLists.txt b/libs/snitch/CMakeLists.txt index 7e377af99d..a89327e3c3 100644 --- a/libs/snitch/CMakeLists.txt +++ b/libs/snitch/CMakeLists.txt @@ -2,6 +2,7 @@ project(snitch) add_library(snitch src/dbus_client.cpp + src/snitch_impl.cpp ) add_library(tfc::snitch ALIAS snitch) target_include_directories(snitch diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index ca78069cea..c0df402888 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -3,16 +3,12 @@ #include #include #include -#include #include #include -#include -#include -#include +#include #include -#include #include #include #include @@ -45,13 +41,12 @@ class alarm { /// \param unique_id A unique identifier for the alarm within this process /// \param default_args Default fmt::arg values to populate the alarm with, e.g. fmt::arg("index", 1) for tank 1 etc. alarm(std::shared_ptr conn, std::string_view unique_id, named_arg auto&&... default_args) - : conn_{ std::move(conn) }, given_id_{ unique_id }, - default_values_{ std::make_pair(default_args.name, fmt::format("{}", default_args.value))... } { + : impl_{ conn, unique_id, description, details, var.resettable, var.lvl, { std::make_pair(default_args.name, fmt::format("{}", default_args.value))... } } + { static_assert(detail::check_all_arguments_named(description), "All arguments must be named"); static_assert(detail::check_all_arguments_no_format(description), "All arguments may not have format specifiers"); [[maybe_unused]] static constexpr int num_args = sizeof...(default_args); static_assert(num_args <= keys_count, "Too many default arguments"); - register_alarm(); } alarm(alarm const&) = delete; alarm& operator=(alarm const&) = delete; @@ -62,96 +57,26 @@ class alarm { template requires(var.resettable) void on_try_reset(callback_t&& callback) { - dbus_client_.on_try_reset_alarm([this, callback](api::alarm_id_t id) { - if (active_ && id == alarm_id_) { - std::invoke(callback); - } - }); - dbus_client_.on_try_reset_all_alarms([this, callback] { - if (active_) { - std::invoke(callback); - } - }); + impl_.on_try_reset(std::forward(callback)); } void set(named_arg auto&&... args) { fmt::dynamic_format_arg_store store; - for (auto const& [key, value] : default_values_) { + for (auto const& [key, value] : impl_.default_values()) { store.push_back(fmt::arg(key.c_str(), value)); } (store.push_back(args), ...); - logger_.debug(fmt::vformat(description, store)); - logger_.debug(fmt::vformat(details, store)); - - auto params{ default_values_ }; - params.emplace(std::make_pair(args.name, fmt::format("{}", args.value))...); - - if (active_) { - logger_.info("alarm already active, not doing anything"); - } - - active_ = true; - - if (alarm_id_) { - dbus_client_.set_alarm(alarm_id_.value(), params, [this](std::error_code const& ec) { - if (ec) { - logger_.error("Failed to set alarm: {}", ec.message()); - // I am both inclined to reset active flag here aswell as not ... I think I will not - // active_ = false; - } - }); - } else { - retry_timer_.expires_after(std::chrono::seconds(1)); - retry_timer_.async_wait([this, args...](std::error_code const& ec) { - if (ec) { - logger_.error("Retry set timer failed: {}", ec.message()); - return; - } - logger_.debug("Retrying set alarm"); - set(args...); - }); - } + std::string description_formatted = fmt::vformat(description, store); + std::string details_formatted = fmt::vformat(details, store); + impl_.set(description_formatted, details_formatted, { std::make_pair(args.name, fmt::format("{}", args.value))... }); } void reset() { - if (!active_) { - logger_.info("alarm already inactive, not doing anything"); - return; - } - active_ = false; - if (!alarm_id_) { - logger_.info("NO alarm ID, forget that alarm ever happened, the timings would be off so would not be valuable information"); - return; - } - dbus_client_.reset_alarm(alarm_id_.value(), [this](std::error_code const& ec) { - if (ec) { - logger_.error("Failed to reset alarm: {}", ec.message()); - // I am both inclined to set active flag here aswell as not ... I think I will not - // active_ = true; - } - }); + impl_.reset(); } private: - void register_alarm() { - dbus_client_.register_alarm(tfc_id_, description, details, var.resettable, var.lvl, - [this](std::error_code const& ec, api::alarm_id_t id) { - if (ec) { - logger_.error("Failed to register alarm: {}", ec.message()); - return; - } - alarm_id_ = id; - }); - } - bool active_{}; - std::shared_ptr conn_; - std::string given_id_; - std::string tfc_id_{ fmt::format("{}.{}.{}", base::get_exe_name(), base::get_proc_name(), given_id_) }; - std::unordered_map default_values_; - std::optional alarm_id_{}; - asio::steady_timer retry_timer_{ conn_->get_io_context() }; - detail::dbus_client dbus_client_{ conn_ }; - logger::logger logger_{ fmt::format("snitch.{}", given_id_) }; + detail::alarm_impl impl_; }; diff --git a/libs/snitch/inc/public/tfc/snitch/common.hpp b/libs/snitch/inc/public/tfc/snitch/common.hpp index 37e7019f47..17ecad12a3 100644 --- a/libs/snitch/inc/public/tfc/snitch/common.hpp +++ b/libs/snitch/inc/public/tfc/snitch/common.hpp @@ -19,6 +19,7 @@ namespace api { using time_point = std::chrono::time_point; using alarm_id_t = std::uint64_t; +using activation_id_t = alarm_id_t; // std::int8_t is not in the dbus spec, so we use std::int16_t instead enum struct active_e : std::int16_t { diff --git a/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp index 7af144827e..9e3f054274 100644 --- a/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp +++ b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp @@ -20,9 +20,9 @@ class dbus_client { auto list_activations(std::string_view locale, std::uint64_t start_count, std::uint64_t count, level_e, api::active_e, api::time_point start, api::time_point end, std::function)> token) -> void; - auto set_alarm(api::alarm_id_t, const std::unordered_map & args, std::function token = [](auto const&){}) -> void; + auto set_alarm(api::alarm_id_t, const std::unordered_map & args, std::function token = [](auto const&, auto){}) -> void; - auto reset_alarm(api::alarm_id_t, std::function token = [](auto const&){}) -> void; + auto reset_alarm(api::activation_id_t, std::function token = [](auto const&){}) -> void; auto try_reset_alarm(api::alarm_id_t, std::function) -> void; diff --git a/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp b/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp new file mode 100644 index 0000000000..46a44001c1 --- /dev/null +++ b/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include +#include + +namespace tfc::logger { +class logger; +} // namespace tfc::logger + +namespace tfc::snitch::detail { + +namespace asio = boost::asio; + +class dbus_client; + +class alarm_impl { +public: + alarm_impl(std::shared_ptr conn, std::string_view unique_id, std::string_view description, std::string_view details, bool resettable, level_e lvl, std::unordered_map&& default_args); + alarm_impl(alarm_impl const&) = delete; + auto operator=(alarm_impl const&) -> alarm_impl& = delete; + alarm_impl(alarm_impl&&) = delete; + auto operator=(alarm_impl&&) -> alarm_impl& = delete; + ~alarm_impl() = default; + + auto const& default_values() const noexcept { return default_values_; } + void on_try_reset(std::function callback); + void set(std::string_view description_formatted, std::string_view details_formatted, std::unordered_map&& args); + void reset(); + +private: + void register_alarm(); + void set(bool new_set, std::unordered_map&& args); + std::optional activation_id_{}; + std::optional alarm_id_{}; + std::string given_id_; + std::string description_; + std::string details_; + bool resettable_{}; + level_e lvl_{ level_e::unknown }; + std::string tfc_id_; + std::unordered_map default_values_; + std::shared_ptr conn_; + std::unique_ptr> dbus_client_; + std::unique_ptr> logger_; + asio::steady_timer retry_timer_; +}; + +} // namespace tfc::snitch::detail diff --git a/libs/snitch/src/dbus_client.cpp b/libs/snitch/src/dbus_client.cpp index debcc3173d..727fa4e5de 100644 --- a/libs/snitch/src/dbus_client.cpp +++ b/libs/snitch/src/dbus_client.cpp @@ -66,13 +66,13 @@ auto dbus_client::list_activations(std::string_view locale, , std::string{ locale }, start_count, count, std::to_underlying(lvl), std::to_underlying(active), start.time_since_epoch().count(), end.time_since_epoch().count() ); } -auto dbus_client::set_alarm(api::alarm_id_t id, const std::unordered_map & args, std::function token) +auto dbus_client::set_alarm(api::alarm_id_t id, const std::unordered_map & args, std::function token) -> void { dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::set_alarm } // arguments , id, args); } -auto dbus_client::reset_alarm(api::alarm_id_t id, std::function token) -> void { +auto dbus_client::reset_alarm(api::activation_id_t id, std::function token) -> void { dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::reset_alarm } // arguments , id); diff --git a/libs/snitch/src/snitch_impl.cpp b/libs/snitch/src/snitch_impl.cpp new file mode 100644 index 0000000000..b3ab0a687b --- /dev/null +++ b/libs/snitch/src/snitch_impl.cpp @@ -0,0 +1,103 @@ + +#include +#include +#include +#include +#include + +namespace tfc::snitch::detail { + +alarm_impl::alarm_impl(std::shared_ptr conn, + std::string_view unique_id, + std::string_view description, + std::string_view details, + bool resettable, + level_e lvl, + std::unordered_map&& default_args) + : given_id_{ unique_id }, description_{ description }, details_{ details }, resettable_{ resettable }, lvl_{ lvl }, tfc_id_{ fmt::format("{}.{}.{}", base::get_exe_name(), base::get_proc_name(), given_id_) }, default_values_{ std::move(default_args) }, + conn_{ std::move(conn) }, dbus_client_{ std::make_unique(conn_) }, + logger_{ std::make_unique(fmt::format("snitch.{}", given_id_)) }, + retry_timer_{ conn_->get_io_context() } { + register_alarm(); +} + +void alarm_impl::on_try_reset(std::function callback) { + dbus_client_->on_try_reset_alarm([this, callback](api::alarm_id_t id) { + if (activation_id_.has_value() && id == alarm_id_) { + std::invoke(callback); + } + }); + dbus_client_->on_try_reset_all_alarms([this, callback] { + if (activation_id_.has_value()) { + std::invoke(callback); + } + }); +} + +void alarm_impl::set(std::string_view description_formatted, std::string_view details_formatted, std::unordered_map&& args) { + if (activation_id_) { + logger_->info("alarm already active with id: {}, not doing anything", activation_id_.value()); + return; + } + + logger_->debug("Description: '{}'", description_formatted); + logger_->debug("Details: '{}'", details_formatted); + + auto params{ default_values_ }; + params.merge(std::move(args)); + + set(true, std::move(params)); +} + +void alarm_impl::set(bool new_set, std::unordered_map&& params) { + if (new_set) { + retry_timer_.cancel(); + } + if (alarm_id_) { + dbus_client_->set_alarm(alarm_id_.value(), params, [this](std::error_code const& ec, api::activation_id_t id) { + if (ec) { + logger_->error("Failed to set alarm: {}", ec.message()); + return; + } + activation_id_ = id; + }); + } else { + retry_timer_.expires_after(std::chrono::seconds(1)); + retry_timer_.async_wait([this, params_mv = std::move(params)](std::error_code const& ec) mutable { + if (ec) { + logger_->error("Retry set timer failed: {}", ec.message()); + return; + } + logger_->debug("Retrying set alarm"); + set(false, std::move(params_mv)); + }); + } +} + +void alarm_impl::reset() { + if (!activation_id_) { + logger_->info("alarm already inactive, not doing anything"); + return; + } + dbus_client_->reset_alarm(activation_id_.value(), [this](std::error_code const& ec) { + if (ec) { + logger_->error("Failed to reset alarm: {}", ec.message()); + return; + } + activation_id_.reset(); + }); +} + + +void alarm_impl::register_alarm() { + dbus_client_->register_alarm(tfc_id_, description_, details_, resettable_, lvl_, + [this](std::error_code const& ec, api::alarm_id_t id) { + if (ec) { + logger_->error("Failed to register alarm: {}", ec.message()); + return; + } + alarm_id_ = id; + }); +} + +} // namespace tfc::snitch::detail From 52e83e3865d4cf20f2379245c4d527edfed11e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Fri, 21 Jun 2024 11:40:32 +0000 Subject: [PATCH 26/42] set now with callback when finished --- libs/snitch/inc/public/tfc/snitch.hpp | 4 +-- .../public/tfc/snitch/details/snitch_impl.hpp | 4 +-- libs/snitch/src/snitch_impl.cpp | 25 +++++++++++-------- libs/snitch/tests/snitch_test.cpp | 2 +- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index c0df402888..e4ed57c197 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -60,7 +60,7 @@ class alarm { impl_.on_try_reset(std::forward(callback)); } - void set(named_arg auto&&... args) { + void set(std::function on_set_finished, named_arg auto&&... args) { fmt::dynamic_format_arg_store store; for (auto const& [key, value] : impl_.default_values()) { store.push_back(fmt::arg(key.c_str(), value)); @@ -68,7 +68,7 @@ class alarm { (store.push_back(args), ...); std::string description_formatted = fmt::vformat(description, store); std::string details_formatted = fmt::vformat(details, store); - impl_.set(description_formatted, details_formatted, { std::make_pair(args.name, fmt::format("{}", args.value))... }); + impl_.set(description_formatted, details_formatted, { std::make_pair(args.name, fmt::format("{}", args.value))... }, std::move(on_set_finished)); } void reset() { diff --git a/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp b/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp index 46a44001c1..2998ad533e 100644 --- a/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp +++ b/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp @@ -31,12 +31,12 @@ class alarm_impl { auto const& default_values() const noexcept { return default_values_; } void on_try_reset(std::function callback); - void set(std::string_view description_formatted, std::string_view details_formatted, std::unordered_map&& args); + void set(std::string_view description_formatted, std::string_view details_formatted, std::unordered_map&& args, std::function&& on_set_finished); void reset(); private: void register_alarm(); - void set(bool new_set, std::unordered_map&& args); + void set(std::unordered_map&& args, std::function&& on_set_finished); std::optional activation_id_{}; std::optional alarm_id_{}; std::string given_id_; diff --git a/libs/snitch/src/snitch_impl.cpp b/libs/snitch/src/snitch_impl.cpp index b3ab0a687b..937ed56f01 100644 --- a/libs/snitch/src/snitch_impl.cpp +++ b/libs/snitch/src/snitch_impl.cpp @@ -1,5 +1,6 @@ #include + #include #include #include @@ -34,7 +35,7 @@ void alarm_impl::on_try_reset(std::function callback) { }); } -void alarm_impl::set(std::string_view description_formatted, std::string_view details_formatted, std::unordered_map&& args) { +void alarm_impl::set(std::string_view description_formatted, std::string_view details_formatted, std::unordered_map&& args, std::function&& on_set_finished) { if (activation_id_) { logger_->info("alarm already active with id: {}, not doing anything", activation_id_.value()); return; @@ -46,30 +47,34 @@ void alarm_impl::set(std::string_view description_formatted, std::string_view de auto params{ default_values_ }; params.merge(std::move(args)); - set(true, std::move(params)); + set(std::move(params), std::move(on_set_finished)); } -void alarm_impl::set(bool new_set, std::unordered_map&& params) { - if (new_set) { +void alarm_impl::set(std::unordered_map&& params, std::function&& on_set_finished) { + auto const now = decltype(retry_timer_)::clock_type::now(); + if (now > retry_timer_.expiry()) { retry_timer_.cancel(); } if (alarm_id_) { - dbus_client_->set_alarm(alarm_id_.value(), params, [this](std::error_code const& ec, api::activation_id_t id) { + dbus_client_->set_alarm(alarm_id_.value(), params, [this, cb = std::move(on_set_finished)](std::error_code const& ec, api::activation_id_t id) { if (ec) { logger_->error("Failed to set alarm: {}", ec.message()); - return; + // todo: should we call test here? + } + else { + activation_id_ = id; } - activation_id_ = id; + std::invoke(cb, ec); }); } else { retry_timer_.expires_after(std::chrono::seconds(1)); - retry_timer_.async_wait([this, params_mv = std::move(params)](std::error_code const& ec) mutable { + retry_timer_.async_wait([this, params_mv = std::move(params), cb = std::move(on_set_finished)](std::error_code const& ec) mutable { if (ec) { - logger_->error("Retry set timer failed: {}", ec.message()); + logger_->info("Retry set timer failed: {}", ec.message()); return; } logger_->debug("Retrying set alarm"); - set(false, std::move(params_mv)); + set(std::move(params_mv), std::move(cb)); }); } } diff --git a/libs/snitch/tests/snitch_test.cpp b/libs/snitch/tests/snitch_test.cpp index 6297a76b09..99e0cfe0f0 100644 --- a/libs/snitch/tests/snitch_test.cpp +++ b/libs/snitch/tests/snitch_test.cpp @@ -52,7 +52,7 @@ auto main(int argc, char** argv) -> int { asio::io_context ctx; auto connection = std::make_shared(ctx, tfc::dbus::sd_bus_open_system()); tfc::snitch::info<"short desc {name}", "long desc {name} {index}"> tank(connection, "unique_id", fmt::arg("name", "hello"), fmt::arg("index", 42)); - tank.set(); + tank.set([](auto){}); // warn.on_ack([]{}); From d8b5364eb641e35ac3dbc5124788d884d0a0ecf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Fri, 21 Jun 2024 11:52:29 +0000 Subject: [PATCH 27/42] minor --- libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp | 1 + libs/snitch/src/snitch_impl.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp b/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp index 2998ad533e..966bf1a90a 100644 --- a/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp +++ b/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include diff --git a/libs/snitch/src/snitch_impl.cpp b/libs/snitch/src/snitch_impl.cpp index 937ed56f01..034ac3d3e1 100644 --- a/libs/snitch/src/snitch_impl.cpp +++ b/libs/snitch/src/snitch_impl.cpp @@ -59,7 +59,7 @@ void alarm_impl::set(std::unordered_map&& params, std: dbus_client_->set_alarm(alarm_id_.value(), params, [this, cb = std::move(on_set_finished)](std::error_code const& ec, api::activation_id_t id) { if (ec) { logger_->error("Failed to set alarm: {}", ec.message()); - // todo: should we call test here? + // todo: should we call set here? } else { activation_id_ = id; From aa242dcdad44dcbb304f0b41fda46dae4079030f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Fri, 21 Jun 2024 14:03:07 +0000 Subject: [PATCH 28/42] alarm id and activation id getter aswell as watch daemon alive state --- .../inc/public/tfc/dbus/match_rules.hpp | 10 +++++--- libs/snitch/inc/public/tfc/snitch.hpp | 9 ++++++++ .../public/tfc/snitch/details/dbus_client.hpp | 3 +++ .../public/tfc/snitch/details/snitch_impl.hpp | 6 ++++- libs/snitch/src/dbus_client.cpp | 23 +++++++++++++++++++ libs/snitch/src/snitch_impl.cpp | 5 ++++ 6 files changed, 52 insertions(+), 4 deletions(-) diff --git a/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp b/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp index 4820d10bb9..2584a80320 100644 --- a/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp +++ b/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace tfc::dbus::match::rules { namespace type { @@ -27,6 +28,7 @@ static constexpr std::string_view member_prefix{ "member=" }; static constexpr std::string_view path_prefix{ "path=" }; static constexpr std::string_view path_namespace_prefix{ "path_namespace=" }; static constexpr std::string_view destination_prefix{ "destination=" }; +static constexpr std::string_view arg_prefix{ "arg" }; template static constexpr std::string_view filter{ stx::string_view_join_v }; @@ -61,9 +63,8 @@ static constexpr std::string_view member{ detail::filter static constexpr std::string_view path{ detail::filter }; @@ -87,6 +88,9 @@ static constexpr std::string_view path_namespace{ detail::filter static constexpr std::string_view destination{ detail::filter }; +template +static constexpr std::string_view arg{ detail::filter>, arg_in> }; + /// \brief make complete dbus match rule /// \tparam service_name service name /// \tparam interface_name interface name diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index e4ed57c197..ee38aabc5c 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -75,6 +75,15 @@ class alarm { impl_.reset(); } + auto alarm_id() const noexcept -> std::optional { + return impl_.alarm_id(); + } + + auto activation_id() const noexcept -> std::optional { + return impl_.activation_id(); + } + + private: detail::alarm_impl impl_; }; diff --git a/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp index 9e3f054274..6681aa2aa6 100644 --- a/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp +++ b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp @@ -31,10 +31,13 @@ class dbus_client { auto on_try_reset_alarm(std::function) -> void; auto on_try_reset_all_alarms(std::function) -> void; + + auto on_daemon_alive(std::function) -> void; private: std::shared_ptr dbus_; std::unique_ptr> try_reset_; std::unique_ptr> try_reset_all_; + std::unique_ptr> daemon_alive_; const std::string service_name_{ api::dbus::service_name }; const std::string interface_name_{ api::dbus::interface_name }; const std::string object_path_{ api::dbus::object_path }; diff --git a/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp b/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp index 966bf1a90a..c121eb44e7 100644 --- a/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp +++ b/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp @@ -30,12 +30,16 @@ class alarm_impl { auto operator=(alarm_impl&&) -> alarm_impl& = delete; ~alarm_impl() = default; - auto const& default_values() const noexcept { return default_values_; } + auto default_values() const noexcept -> auto const& { return default_values_; } void on_try_reset(std::function callback); void set(std::string_view description_formatted, std::string_view details_formatted, std::unordered_map&& args, std::function&& on_set_finished); void reset(); + auto alarm_id() const noexcept -> std::optional { return alarm_id_; } + auto activation_id() const noexcept -> std::optional { return activation_id_; } + private: + void on_daemon_alive(); void register_alarm(); void set(std::unordered_map&& args, std::function&& on_set_finished); std::optional activation_id_{}; diff --git a/libs/snitch/src/dbus_client.cpp b/libs/snitch/src/dbus_client.cpp index 727fa4e5de..5d8ff89923 100644 --- a/libs/snitch/src/dbus_client.cpp +++ b/libs/snitch/src/dbus_client.cpp @@ -13,6 +13,24 @@ namespace tfc::snitch::detail { static constexpr auto try_reset_match_{ dbus::match::rules::make_match_rule() }; static constexpr auto try_reset_all_match_{ dbus::match::rules::make_match_rule() }; +using std::string_view_literals::operator""sv; +static constexpr std::string_view dbus_service_name = "org.freedesktop.DBus"sv; +static constexpr std::string_view dbus_interface = "org.freedesktop.DBus"sv; +static constexpr std::string_view dbus_path = "/org/freedesktop/DBus"sv; +static constexpr std::string_view dbus_signal = "NameOwnerChanged"sv; +static constexpr std::string_view dbus_arg0 = api::dbus::service_name; +static constexpr std::string_view daemon_alive_match_{ + stx::string_view_join_v, + dbus::match::rules::interface, + dbus::match::rules::path, + dbus::match::rules::member, + dbus::match::rules::arg<0, dbus_arg0> + > +}; + + + dbus_client::dbus_client(std::shared_ptr conn) : dbus_{ std::move(conn) } {} auto dbus_client::register_alarm(std::string_view tfc_id, std::string_view description, @@ -98,5 +116,10 @@ auto dbus_client::on_try_reset_all_alarms(std::function token) -> void { std::invoke(token_mv); }); } +auto dbus_client::on_daemon_alive(std::function token) -> void { + daemon_alive_ = std::make_unique(*dbus_, daemon_alive_match_.data(), [token_mv = std::move(token)](sdbusplus::message_t&) { + std::invoke(token_mv); + }); +} } // namespace tfc::snitch::detail diff --git a/libs/snitch/src/snitch_impl.cpp b/libs/snitch/src/snitch_impl.cpp index 034ac3d3e1..a6ad4fad16 100644 --- a/libs/snitch/src/snitch_impl.cpp +++ b/libs/snitch/src/snitch_impl.cpp @@ -19,6 +19,7 @@ alarm_impl::alarm_impl(std::shared_ptr conn, conn_{ std::move(conn) }, dbus_client_{ std::make_unique(conn_) }, logger_{ std::make_unique(fmt::format("snitch.{}", given_id_)) }, retry_timer_{ conn_->get_io_context() } { + dbus_client_->on_daemon_alive([this]{ this->on_daemon_alive(); }); register_alarm(); } @@ -105,4 +106,8 @@ void alarm_impl::register_alarm() { }); } +void alarm_impl::on_daemon_alive() { + register_alarm(); +} + } // namespace tfc::snitch::detail From 5da4ed6eb5d80877da7ba6813941849e2fd23a39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Fri, 21 Jun 2024 14:15:57 +0000 Subject: [PATCH 29/42] retry set as quickly as when alarm id is known --- libs/snitch/src/snitch_impl.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libs/snitch/src/snitch_impl.cpp b/libs/snitch/src/snitch_impl.cpp index a6ad4fad16..cee4740391 100644 --- a/libs/snitch/src/snitch_impl.cpp +++ b/libs/snitch/src/snitch_impl.cpp @@ -70,7 +70,10 @@ void alarm_impl::set(std::unordered_map&& params, std: } else { retry_timer_.expires_after(std::chrono::seconds(1)); retry_timer_.async_wait([this, params_mv = std::move(params), cb = std::move(on_set_finished)](std::error_code const& ec) mutable { - if (ec) { + if (ec == std::errc::operation_canceled && alarm_id_.has_value()) { + // timer was cancelled, but alarm id was set in the meantime + } + else if (ec) { logger_->info("Retry set timer failed: {}", ec.message()); return; } @@ -103,6 +106,7 @@ void alarm_impl::register_alarm() { return; } alarm_id_ = id; + retry_timer_.cancel(); }); } From 568efc910fe67982e647f4ededc7897266603ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Fri, 21 Jun 2024 14:24:59 +0000 Subject: [PATCH 30/42] Added skeleton for integration test --- exes/themis/inc/alarm_database.hpp | 8 +-- exes/themis/inc/dbus_interface.hpp | 9 ++- exes/themis/tests/CMakeLists.txt | 19 +++++- exes/themis/tests/themis_integration_test.cpp | 62 +++++++++++++++++++ libs/snitch/inc/public/tfc/snitch.hpp | 1 + libs/snitch/inc/public/tfc/snitch/common.hpp | 1 - 6 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 exes/themis/tests/themis_integration_test.cpp diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index f40088319f..9c177fd063 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -281,7 +281,7 @@ LEFT OUTER JOIN AlarmTranslations as primary_text on (Alarms.alarm_id = primary_ LEFT OUTER JOIN AlarmTranslations as backup_text on (Alarms.alarm_id = backup_text.alarm_id and backup_text.locale = 'en') WHERE activation_time >= {} AND activation_time <= {})", locale, milliseconds_since_epoch(start), milliseconds_since_epoch(end)); - if (level != tfc::snitch::level_e::unknown) { + if (level != tfc::snitch::level_e::all) { populated_query += fmt::format(" AND alarm_level = {}", static_cast(level)); } if (active != tfc::snitch::api::active_e::all) { @@ -295,8 +295,8 @@ WHERE activation_time >= {} AND activation_time <= {})", std::int64_t activation_time, std::optional reset_time, bool activation_level, std::optional primary_details, std::optional primary_description, std::optional backup_details, - std::optional backup_description, std::optional tlocale, - bool alarm_latching, std::uint8_t alarm_level) { + std::optional backup_description, + bool alarm_latching, std::int8_t alarm_level) { if (!backup_description.has_value() || !backup_details.has_value()) { throw std::runtime_error("Backup message not found for alarm translation. This should never happen."); } @@ -307,7 +307,7 @@ WHERE activation_time >= {} AND activation_time <= {})", if (reset_time.has_value()) { final_reset_time = timepoint_from_milliseconds(reset_time.value()); } - activations.emplace_back(alarm_id, activation_id, description, details, tlocale.value(), activation_level, + activations.emplace_back(alarm_id, activation_id, description, details, activation_level, static_cast(alarm_level), alarm_latching, timepoint_from_milliseconds(activation_time), final_reset_time, in_locale); }; diff --git a/exes/themis/inc/dbus_interface.hpp b/exes/themis/inc/dbus_interface.hpp index 19a1423e56..9e2ba08bfa 100644 --- a/exes/themis/inc/dbus_interface.hpp +++ b/exes/themis/inc/dbus_interface.hpp @@ -8,22 +8,21 @@ #include #include #include +#include #include #include #include +#include namespace tfc::themis { -using tfc::ipc::details::type_e; - -using dbus_error = tfc::dbus::exception::runtime; using namespace tfc::snitch::api::dbus; using std::string_view_literals::operator""sv; class interface { public: - explicit interface(boost::asio::io_context& ctx, tfc::themis::alarm_database& database) { - connection_ = std::make_shared(ctx, tfc::dbus::sd_bus_open_system()); + explicit interface(std::shared_ptr connection, tfc::themis::alarm_database& database) { + connection_ = connection; object_server_ = std::make_unique(connection_); connection_->request_name(service_name.data()); interface_ = diff --git a/exes/themis/tests/CMakeLists.txt b/exes/themis/tests/CMakeLists.txt index c4371f5685..a8d884e269 100644 --- a/exes/themis/tests/CMakeLists.txt +++ b/exes/themis/tests/CMakeLists.txt @@ -2,11 +2,11 @@ find_package(ut CONFIG REQUIRED) find_package(GTest CONFIG REQUIRED COMPONENTS gmock) find_path(BEXT_SML_INCLUDE_DIRS "boost/sml.hpp") -add_executable(themis_database_test themis_database_test.cpp) find_package(unofficial-sqlite3 CONFIG REQUIRED) find_package(OpenSSL CONFIG REQUIRED) find_path(SQLITE_MODERN_CPP_INCLUDE_DIRS "sqlite_modern_cpp.h") +add_executable(themis_database_test themis_database_test.cpp) target_link_libraries(themis_database_test PRIVATE Boost::ut @@ -19,4 +19,19 @@ target_link_libraries(themis_database_test target_include_directories(themis_database_test PRIVATE ../inc -) \ No newline at end of file +) + +add_executable(themis_integration_test themis_integration_test.cpp) +target_link_libraries(themis_integration_test + PRIVATE + Boost::ut + tfc::base + tfc::snitch + unofficial::sqlite3::sqlite3 + OpenSSL::Crypto +) + +target_include_directories(themis_integration_test + PRIVATE + ../inc +) diff --git a/exes/themis/tests/themis_integration_test.cpp b/exes/themis/tests/themis_integration_test.cpp new file mode 100644 index 0000000000..4d82f604e3 --- /dev/null +++ b/exes/themis/tests/themis_integration_test.cpp @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ut = boost::ut; +namespace asio = boost::asio; +using namespace std::chrono_literals; + +using boost::ut::operator""_test; +using boost::ut::operator|; +using boost::ut::operator/; +using boost::ut::expect; +using boost::ut::throws; +using tfc::snitch::error; +using tfc::snitch::info; +using tfc::snitch::warning; +using tfc::themis::alarm_database; +using tfc::themis::interface; + +struct test_setup { + asio::io_context ctx; + alarm_database db; + std::shared_ptr connection; + interface face; + std::array ran{}; + std::array ids{}; + test_setup() + : db(false), connection{ std::make_shared(ctx, tfc::dbus::sd_bus_open_system()) }, + face(connection, db) {} +}; + +int main(int argc, char** argv) { + tfc::base::init(argc, argv); + "Basics working"_test = [] { + test_setup t; + info<"desc", "details"> i(t.connection, "first_test"); + i.set([&](const std::error_code& err) { + expect(!err) << err.message(); + t.ran[0] = true; + t.ctx.stop(); + }); + t.ctx.run_for(1500ms); + expect(t.ran[0]); + expect(t.db.list_alarms().size() == 1); + + auto activations = t.db.list_activations( + "en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); + expect(activations.size() == 1); + expect(activations.at(0).lvl == tfc::snitch::level_e::info) << "lvl: " << static_cast(activations.at(0).lvl); + i.reset(); + }; + return 0; +} diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index ee38aabc5c..58bbb93b00 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include diff --git a/libs/snitch/inc/public/tfc/snitch/common.hpp b/libs/snitch/inc/public/tfc/snitch/common.hpp index 17ecad12a3..91328a9597 100644 --- a/libs/snitch/inc/public/tfc/snitch/common.hpp +++ b/libs/snitch/inc/public/tfc/snitch/common.hpp @@ -48,7 +48,6 @@ struct activation { std::uint64_t activation_id; std::string description; std::string details; - std::string locale; bool active{}; level_e lvl{ level_e::unknown }; bool latching{}; From ceae13535262875ab44f722439edafe077ce4b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Fri, 21 Jun 2024 14:28:40 +0000 Subject: [PATCH 31/42] comment line which broke the code --- libs/snitch/src/snitch_impl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/snitch/src/snitch_impl.cpp b/libs/snitch/src/snitch_impl.cpp index cee4740391..821f912c05 100644 --- a/libs/snitch/src/snitch_impl.cpp +++ b/libs/snitch/src/snitch_impl.cpp @@ -19,7 +19,7 @@ alarm_impl::alarm_impl(std::shared_ptr conn, conn_{ std::move(conn) }, dbus_client_{ std::make_unique(conn_) }, logger_{ std::make_unique(fmt::format("snitch.{}", given_id_)) }, retry_timer_{ conn_->get_io_context() } { - dbus_client_->on_daemon_alive([this]{ this->on_daemon_alive(); }); + // dbus_client_->on_daemon_alive([this]{ this->on_daemon_alive(); }); register_alarm(); } From 8cc005334b0b8c0633df0dcb1a87d309100032a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Fri, 21 Jun 2024 14:37:26 +0000 Subject: [PATCH 32/42] reset with callable and fix dbus arg matcher --- libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp | 3 ++- libs/snitch/inc/public/tfc/snitch.hpp | 4 ++-- .../inc/public/tfc/snitch/details/snitch_impl.hpp | 2 +- libs/snitch/src/snitch_impl.cpp | 12 +++++++----- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp b/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp index 2584a80320..0a2d82aa70 100644 --- a/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp +++ b/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp @@ -29,6 +29,7 @@ static constexpr std::string_view path_prefix{ "path=" }; static constexpr std::string_view path_namespace_prefix{ "path_namespace=" }; static constexpr std::string_view destination_prefix{ "destination=" }; static constexpr std::string_view arg_prefix{ "arg" }; +static constexpr std::string_view equal{ "=" }; template static constexpr std::string_view filter{ stx::string_view_join_v }; @@ -89,7 +90,7 @@ template static constexpr std::string_view destination{ detail::filter }; template -static constexpr std::string_view arg{ detail::filter>, arg_in> }; +static constexpr std::string_view arg{ detail::filter, detail::equal>, arg_in> }; /// \brief make complete dbus match rule /// \tparam service_name service name diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index 58bbb93b00..371ca293bd 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -72,8 +72,8 @@ class alarm { impl_.set(description_formatted, details_formatted, { std::make_pair(args.name, fmt::format("{}", args.value))... }, std::move(on_set_finished)); } - void reset() { - impl_.reset(); + void reset(std::function on_reset_finished = [](auto){}) { + impl_.reset(std::move(on_reset_finished)); } auto alarm_id() const noexcept -> std::optional { diff --git a/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp b/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp index c121eb44e7..2301aed1d1 100644 --- a/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp +++ b/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp @@ -33,7 +33,7 @@ class alarm_impl { auto default_values() const noexcept -> auto const& { return default_values_; } void on_try_reset(std::function callback); void set(std::string_view description_formatted, std::string_view details_formatted, std::unordered_map&& args, std::function&& on_set_finished); - void reset(); + void reset(std::function&& on_reset_finished); auto alarm_id() const noexcept -> std::optional { return alarm_id_; } auto activation_id() const noexcept -> std::optional { return activation_id_; } diff --git a/libs/snitch/src/snitch_impl.cpp b/libs/snitch/src/snitch_impl.cpp index 821f912c05..319b64a320 100644 --- a/libs/snitch/src/snitch_impl.cpp +++ b/libs/snitch/src/snitch_impl.cpp @@ -19,7 +19,7 @@ alarm_impl::alarm_impl(std::shared_ptr conn, conn_{ std::move(conn) }, dbus_client_{ std::make_unique(conn_) }, logger_{ std::make_unique(fmt::format("snitch.{}", given_id_)) }, retry_timer_{ conn_->get_io_context() } { - // dbus_client_->on_daemon_alive([this]{ this->on_daemon_alive(); }); + dbus_client_->on_daemon_alive([this]{ this->on_daemon_alive(); }); register_alarm(); } @@ -83,17 +83,19 @@ void alarm_impl::set(std::unordered_map&& params, std: } } -void alarm_impl::reset() { +void alarm_impl::reset(std::function&& on_reset_finished) { if (!activation_id_) { logger_->info("alarm already inactive, not doing anything"); return; } - dbus_client_->reset_alarm(activation_id_.value(), [this](std::error_code const& ec) { + dbus_client_->reset_alarm(activation_id_.value(), [this, cb = std::move(on_reset_finished)](std::error_code const& ec) { if (ec) { logger_->error("Failed to reset alarm: {}", ec.message()); - return; } - activation_id_.reset(); + else { + activation_id_.reset(); + } + std::invoke(cb, ec); }); } From 22cc7dbd34dab705ddc1ff4fbd8d14acc896ce30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Fri, 21 Jun 2024 14:58:14 +0000 Subject: [PATCH 33/42] Some basic tests working --- exes/themis/inc/dbus_interface.hpp | 4 ++- exes/themis/tests/themis_integration_test.cpp | 36 ++++++++++++++----- .../inc/public/tfc/dbus/sdbusplus_meta.hpp | 6 +++- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/exes/themis/inc/dbus_interface.hpp b/exes/themis/inc/dbus_interface.hpp index 9e2ba08bfa..78e8ac662f 100644 --- a/exes/themis/inc/dbus_interface.hpp +++ b/exes/themis/inc/dbus_interface.hpp @@ -58,8 +58,10 @@ class interface { auto message = interface_->new_signal(signals::try_reset_all.data()); message.signal_send(); }); + using tfc::snitch::api::active_e; + using tfc::snitch::level_e; interface_->register_method(std::string(methods::list_activations), - [&](const std::string& locale, std::uint64_t start_count, std::uint64_t count, int alarm_level, int active, int64_t start, int64_t end) -> std::string { + [&](const std::string& locale, std::uint64_t start_count, std::uint64_t count, std::underlying_type_t alarm_level, std::underlying_type_t active, int64_t start, int64_t end) -> std::string { auto cstart = tfc::themis::alarm_database::timepoint_from_milliseconds(start); auto cend = tfc::themis::alarm_database::timepoint_from_milliseconds(end); return glz::write_json(database.list_activations(locale, start_count, count, static_cast(alarm_level), static_cast(active), cstart, cend)); diff --git a/exes/themis/tests/themis_integration_test.cpp b/exes/themis/tests/themis_integration_test.cpp index 4d82f604e3..7f6373ff40 100644 --- a/exes/themis/tests/themis_integration_test.cpp +++ b/exes/themis/tests/themis_integration_test.cpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace ut = boost::ut; namespace asio = boost::asio; @@ -21,19 +22,31 @@ using boost::ut::throws; using tfc::snitch::error; using tfc::snitch::info; using tfc::snitch::warning; +using tfc::snitch::detail::dbus_client; using tfc::themis::alarm_database; using tfc::themis::interface; struct test_setup { asio::io_context ctx; + asio::io_context sctx; alarm_database db; + std::shared_ptr sconnection; std::shared_ptr connection; interface face; std::array ran{}; std::array ids{}; + dbus_client client; + std::thread t; test_setup() - : db(false), connection{ std::make_shared(ctx, tfc::dbus::sd_bus_open_system()) }, - face(connection, db) {} + : db(true), sconnection{ std::make_shared(sctx, tfc::dbus::sd_bus_open_system()) }, + connection{ std::make_shared(ctx, tfc::dbus::sd_bus_open_system()) }, + face(connection, db), client(connection), t([&]{ + sctx.run(); + }){} + ~test_setup() { + sctx.stop(); + t.join(); + } }; int main(int argc, char** argv) { @@ -44,18 +57,23 @@ int main(int argc, char** argv) { i.set([&](const std::error_code& err) { expect(!err) << err.message(); t.ran[0] = true; - t.ctx.stop(); }); - t.ctx.run_for(1500ms); + t.ctx.run_for(2ms); expect(t.ran[0]); - expect(t.db.list_alarms().size() == 1); + // expect(t.db.list_alarms().size() == 1); - auto activations = t.db.list_activations( + t.client.list_activations( "en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, tfc::themis::alarm_database::timepoint_from_milliseconds(0), - tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); - expect(activations.size() == 1); - expect(activations.at(0).lvl == tfc::snitch::level_e::info) << "lvl: " << static_cast(activations.at(0).lvl); + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max()), + [&](const std::error_code& err, std::vector act) { + expect(!err) << err.message(); + expect(act.size() == 1); + expect(act.at(0).lvl == tfc::snitch::level_e::info); + t.ran[1] = true; + }); + t.ctx.run_for(2ms); + expect(t.ran[1]); i.reset(); }; return 0; diff --git a/libs/dbus_util/inc/public/tfc/dbus/sdbusplus_meta.hpp b/libs/dbus_util/inc/public/tfc/dbus/sdbusplus_meta.hpp index 67062c064c..29b05c637a 100644 --- a/libs/dbus_util/inc/public/tfc/dbus/sdbusplus_meta.hpp +++ b/libs/dbus_util/inc/public/tfc/dbus/sdbusplus_meta.hpp @@ -24,7 +24,11 @@ template struct type_id; template <> -struct type_id : tuple_type_id +struct type_id : tuple_type_id +{}; + +template <> +struct type_id : tuple_type_id {}; template From 65ed3fda911a03f658509a28a4beeb56acfea896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Fri, 21 Jun 2024 15:44:38 +0000 Subject: [PATCH 34/42] Added some integration tests --- exes/themis/inc/alarm_database.hpp | 21 ++- exes/themis/inc/dbus_interface.hpp | 20 ++- exes/themis/tests/themis_integration_test.cpp | 142 +++++++++++++++++- libs/snitch/inc/public/tfc/snitch.hpp | 2 +- 4 files changed, 167 insertions(+), 18 deletions(-) diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index 9c177fd063..1b9a373aa0 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -14,11 +14,13 @@ #include #include #include +#include namespace tfc::themis { using enum tfc::snitch::level_e; using tfc::snitch::api::time_point; +using dbus_error = tfc::dbus::exception::runtime; class error_log { public: @@ -115,7 +117,7 @@ CREATE TABLE IF NOT EXISTS AlarmVariables( ms_count_registered_at); auto insert_id = db_.last_insert_rowid(); if (insert_id < 0) { - throw std::runtime_error("Failed to insert alarm into database"); + throw dbus_error("Failed to insert alarm into database"); } alarm_id = static_cast(insert_id); add_alarm_translation(alarm_id, "en", description, details); @@ -199,6 +201,13 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; return count > 0; } + [[nodiscard]] auto count_active_alarms() -> std::int64_t { + std::int64_t count = 0; + db_ << fmt::format("SELECT COUNT(*) FROM AlarmActivations WHERE activation_level = 1;") >> + [&](std::int64_t c) { count = c; }; + return count; + } + [[nodiscard]] auto is_activation_high(snitch::api::alarm_id_t activation_id) -> bool { bool active = false; db_ << fmt::format("SELECT activation_level FROM AlarmActivations WHERE activation_id = {} AND activation_level = 1;", @@ -213,6 +222,10 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; return count; } + [[nodiscard]] auto is_some_alarm_active() -> bool { + return active_alarm_count() > 0; + } + /** * @brief Set an alarm in the database * @param alarm_id the id of the alarm @@ -224,7 +237,7 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; const std::unordered_map& variables, std::optional tp = {}) -> std::uint64_t { if (is_alarm_active(alarm_id)) { - throw std::runtime_error("Alarm is already active"); + throw dbus_error("Alarm is already active"); } db_ << "BEGIN;"; std::uint64_t activation_id; @@ -246,7 +259,7 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; } auto reset_alarm(snitch::api::alarm_id_t activation_id, std::optional tp = {}) -> void { if (!is_activation_high(activation_id)) { - throw std::runtime_error("Cannot reset an inactive activation"); + throw dbus_error("Cannot reset an inactive activation"); } db_ << fmt::format("UPDATE AlarmActivations SET activation_level = 0, reset_time = {} WHERE activation_id = {};", milliseconds_since_epoch(tp), activation_id); @@ -298,7 +311,7 @@ WHERE activation_time >= {} AND activation_time <= {})", std::optional backup_description, bool alarm_latching, std::int8_t alarm_level) { if (!backup_description.has_value() || !backup_details.has_value()) { - throw std::runtime_error("Backup message not found for alarm translation. This should never happen."); + throw dbus_error("Backup message not found for alarm translation. This should never happen."); } std::string details = primary_details.value_or(backup_details.value()); std::string description = primary_description.value_or(backup_description.value()); diff --git a/exes/themis/inc/dbus_interface.hpp b/exes/themis/inc/dbus_interface.hpp index 78e8ac662f..70f7aa7458 100644 --- a/exes/themis/inc/dbus_interface.hpp +++ b/exes/themis/inc/dbus_interface.hpp @@ -13,11 +13,13 @@ #include #include #include +#include namespace tfc::themis { using namespace tfc::snitch::api::dbus; using std::string_view_literals::operator""sv; +using dbus_error = tfc::dbus::exception::runtime; class interface { public: @@ -49,14 +51,22 @@ class interface { }); interface_->register_method(std::string(methods::try_reset), [&](snitch::api::alarm_id_t alarm_id) -> void { - auto message = interface_->new_signal(signals::try_reset.data()); - message.append(alarm_id); - message.signal_send(); + if (database.is_alarm_active(alarm_id)){ + auto message = interface_->new_signal(signals::try_reset.data()); + message.append(alarm_id); + message.signal_send(); + } else { + throw dbus_error("Alarm is not active"); + } }); interface_->register_method(std::string(methods::try_reset_all), [&]() -> void { - auto message = interface_->new_signal(signals::try_reset_all.data()); - message.signal_send(); + if(database.is_some_alarm_active()){ + auto message = interface_->new_signal(signals::try_reset_all.data()); + message.signal_send(); + } else { + throw dbus_error("No alarm is active"); + } }); using tfc::snitch::api::active_e; using tfc::snitch::level_e; diff --git a/exes/themis/tests/themis_integration_test.cpp b/exes/themis/tests/themis_integration_test.cpp index 7f6373ff40..bee991182b 100644 --- a/exes/themis/tests/themis_integration_test.cpp +++ b/exes/themis/tests/themis_integration_test.cpp @@ -22,6 +22,9 @@ using boost::ut::throws; using tfc::snitch::error; using tfc::snitch::info; using tfc::snitch::warning; +using tfc::snitch::warning_latched; +using tfc::snitch::warning_resettable; +using tfc::snitch::error; using tfc::snitch::detail::dbus_client; using tfc::themis::alarm_database; using tfc::themis::interface; @@ -40,18 +43,16 @@ struct test_setup { test_setup() : db(true), sconnection{ std::make_shared(sctx, tfc::dbus::sd_bus_open_system()) }, connection{ std::make_shared(ctx, tfc::dbus::sd_bus_open_system()) }, - face(connection, db), client(connection), t([&]{ - sctx.run(); - }){} + face(sconnection, db), client(connection), t([&] { sctx.run(); }) {} ~test_setup() { - sctx.stop(); - t.join(); + sctx.stop(); + t.join(); } }; int main(int argc, char** argv) { tfc::base::init(argc, argv); - "Basics working"_test = [] { + "Test some dbus functions for signature errors and general correctness"_test = [] { test_setup t; info<"desc", "details"> i(t.connection, "first_test"); i.set([&](const std::error_code& err) { @@ -60,7 +61,13 @@ int main(int argc, char** argv) { }); t.ctx.run_for(2ms); expect(t.ran[0]); - // expect(t.db.list_alarms().size() == 1); + t.client.list_alarms([&](const std::error_code& err, std::vector alarms){ + expect(!err) << err.message(); + expect(alarms.size() == 1); + t.ran[5] = true; + }); + t.ctx.run_for(2ms); + expect(t.ran[5]); t.client.list_activations( "en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, @@ -70,11 +77,130 @@ int main(int argc, char** argv) { expect(!err) << err.message(); expect(act.size() == 1); expect(act.at(0).lvl == tfc::snitch::level_e::info); + expect(!act.at(0).reset_timestamp.has_value()); t.ran[1] = true; }); t.ctx.run_for(2ms); expect(t.ran[1]); - i.reset(); + i.reset([&](const std::error_code& err) { + expect(!err) << err.message(); + t.ran[2] = true; + }); + t.ctx.run_for(2ms); + expect(t.ran[2]); + t.client.list_activations( + "en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max()), + [&](const std::error_code& err, std::vector act) { + expect(!err) << err.message(); + expect(act.size() == 0); + t.ran[3] = true; + }); + t.client.list_activations( + "en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::inactive, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max()), + [&](const std::error_code& err, std::vector act) { + expect(!err) << err.message(); + expect(act.size() == 1); + expect(act.at(0).reset_timestamp.has_value()); + t.ran[4] = true; + }); + t.ctx.run_for(3ms); + expect(t.ran[3]); + expect(t.ran[4]); + }; + "Test if the try_reset signal makes it in this world"_test = []{ + test_setup t; + warning_latched<"desc", "details"> w(t.connection, "first_test"); + t.ctx.run_for(1ms); + w.on_try_reset([&]() { + t.ran[1] = true; + }); + t.client.try_reset_alarm(w.alarm_id().value(), [&](const std::error_code& err){ + expect(!!err); + t.ran[2] = true; + }); + t.ctx.run_for(2ms); + expect(!t.ran[1]); + expect(t.ran[2]); + + w.set([&](const std::error_code& err) { + expect(!err) << err.message(); + t.ran[0] = true; + }); + t.ctx.run_for(2ms); + expect(t.ran[0]); + expect(w.alarm_id().has_value()); + w.on_try_reset([&]() { + t.ran[3] = true; + }); + t.client.try_reset_alarm(w.alarm_id().value(), [&](const std::error_code& err){ + expect(!err) << err.message(); + t.ran[4] = true; + }); + t.ctx.run_for(2ms); + expect(t.ran[3]); + expect(t.ran[4]); + }; + "Test is the try_reset_all signal tickles these fancies"_test = []{ + test_setup t; + warning_latched<"desc1", "details"> wl(t.connection, "first_test"); + warning_resettable<"desc2", "details"> wr(t.connection, "first_test"); + error<"desc3", "details"> e(t.connection, "first_test"); + wl.on_try_reset([&]() { + t.ran[1] = true; + }); + wr.on_try_reset([&]() { + t.ran[2] = true; + }); + e.on_try_reset([&]() { + t.ran[3] = true; + }); + t.client.try_reset_all_alarms([&](const std::error_code& err){ + expect(!!err); + t.ran[0] = true; + }); + t.ctx.run_for(2ms); + expect(t.ran[0]); + expect(!t.ran[1]); + expect(!t.ran[2]); + expect(!t.ran[3]); + + wl.set([&](const std::error_code& err) { + expect(!err) << err.message(); + t.ran[4] = true; + }); + t.client.try_reset_all_alarms([&](const std::error_code& err){ + expect(!err) << err.message(); + t.ran[5] = true; + }); + t.ctx.run_for(2ms); + expect(t.ran[4]); + expect(t.ran[5]); + expect(t.ran[1]); + + t.ran[1] = false; + wr.set([&](const std::error_code& err) { + expect(!err) << err.message(); + t.ran[6] = true; + }); + e.set([&](const std::error_code& err) { + expect(!err) << err.message(); + t.ran[7] = true; + }); + t.client.try_reset_all_alarms([&](const std::error_code& err){ + expect(!err) << err.message(); + t.ran[8] = true; + }); + t.ctx.run_for(2ms); + expect(t.ran[6]); + expect(t.ran[7]); + expect(t.ran[8]); + expect(t.ran[1]); + expect(t.ran[2]); + expect(t.ran[3]); }; return 0; } diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index 371ca293bd..89e28bec43 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -100,6 +100,6 @@ using warning_latched = alarm using warning_resettable = warning_latched; template -using error = alarm; +using error = alarm; } // namespace tfc::snitch From b730de6fbd65c9a2279124d53e9b3707bc70fe74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Mon, 24 Jun 2024 08:18:03 +0000 Subject: [PATCH 35/42] set alarm without callable when finished --- libs/snitch/inc/public/tfc/snitch.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index 89e28bec43..5283650fa0 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -61,6 +61,10 @@ class alarm { impl_.on_try_reset(std::forward(callback)); } + void set(named_arg auto&&... args) { + set([](auto){}, std::forward(args)...); + } + void set(std::function on_set_finished, named_arg auto&&... args) { fmt::dynamic_format_arg_store store; for (auto const& [key, value] : impl_.default_values()) { From ddd844bcc7066789af40d23f132a03888d676c34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Mon, 24 Jun 2024 08:27:21 +0000 Subject: [PATCH 36/42] minor cleanup and get things compiling --- exes/themis/inc/alarm_database.hpp | 12 +++++++----- exes/themis/src/main.cpp | 7 ++++--- exes/themis/tests/themis_integration_test.cpp | 9 +++++---- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index 1b9a373aa0..51e9e4bbf3 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -1,16 +1,18 @@ #pragma once -#include -#include -#include -#include -#include #include #include #include #include #include #include + +#include +#include +#include +#include +#include + #include #include #include diff --git a/exes/themis/src/main.cpp b/exes/themis/src/main.cpp index aeb444de73..864c98a5f6 100644 --- a/exes/themis/src/main.cpp +++ b/exes/themis/src/main.cpp @@ -1,8 +1,8 @@ #include #include #include -#include -#include + +#include #include #include @@ -17,12 +17,13 @@ auto main(int argc, char** argv) -> int { tfc::base::init(argc, argv, description); asio::io_context ctx; + auto connection = std::make_shared(ctx, tfc::dbus::sd_bus_open_system()); // Initialize the database tfc::themis::alarm_database db(in_memory); // Initialize the IPC server - tfc::themis::interface i(ctx, db); + tfc::themis::interface i(connection, db); ctx.run(); return 0; diff --git a/exes/themis/tests/themis_integration_test.cpp b/exes/themis/tests/themis_integration_test.cpp index bee991182b..e7e1ab683d 100644 --- a/exes/themis/tests/themis_integration_test.cpp +++ b/exes/themis/tests/themis_integration_test.cpp @@ -1,9 +1,10 @@ -#include +#include + #include #include -#include -#include -#include + +#include "alarm_database.hpp" +#include "dbus_interface.hpp" #include #include #include From 86c2c1308071f43cd32ab1fc2c741406c0c66ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Mon, 24 Jun 2024 08:38:07 +0000 Subject: [PATCH 37/42] add test for invalid alarm set --- exes/themis/tests/themis_integration_test.cpp | 13 +++++++++++++ libs/snitch/inc/public/tfc/snitch.hpp | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/exes/themis/tests/themis_integration_test.cpp b/exes/themis/tests/themis_integration_test.cpp index e7e1ab683d..5f6776f30c 100644 --- a/exes/themis/tests/themis_integration_test.cpp +++ b/exes/themis/tests/themis_integration_test.cpp @@ -203,5 +203,18 @@ int main(int argc, char** argv) { expect(t.ran[2]); expect(t.ran[3]); }; + + "alarm with declared params but no params provided"_test = [] { + test_setup t; + info<"desc {foo}", "details {bar} {foo}"> i(t.connection, "first_test"); + try { + i.set(); + expect(false) << "Should have thrown"; + } + catch (std::exception&) { + expect(true); + } + }; + return 0; } diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index 5283650fa0..5b927a0820 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -44,7 +44,7 @@ class alarm { alarm(std::shared_ptr conn, std::string_view unique_id, named_arg auto&&... default_args) : impl_{ conn, unique_id, description, details, var.resettable, var.lvl, { std::make_pair(default_args.name, fmt::format("{}", default_args.value))... } } { - static_assert(detail::check_all_arguments_named(description), "All arguments must be named"); + static_assert(detail::check_all_arguments_named(description), "All arguments must be named, e.g. {name}"); static_assert(detail::check_all_arguments_no_format(description), "All arguments may not have format specifiers"); [[maybe_unused]] static constexpr int num_args = sizeof...(default_args); static_assert(num_args <= keys_count, "Too many default arguments"); From 29086ca1039130b11c2658065622788e6ef63e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20Bjarnason?= Date: Mon, 24 Jun 2024 08:55:29 +0000 Subject: [PATCH 38/42] formatting and adding test with arguments --- exes/themis/tests/themis_integration_test.cpp | 90 ++++++++++++------- 1 file changed, 57 insertions(+), 33 deletions(-) diff --git a/exes/themis/tests/themis_integration_test.cpp b/exes/themis/tests/themis_integration_test.cpp index 5f6776f30c..f2a2a7c428 100644 --- a/exes/themis/tests/themis_integration_test.cpp +++ b/exes/themis/tests/themis_integration_test.cpp @@ -3,13 +3,13 @@ #include #include -#include "alarm_database.hpp" -#include "dbus_interface.hpp" #include #include #include #include #include +#include "alarm_database.hpp" +#include "dbus_interface.hpp" namespace ut = boost::ut; namespace asio = boost::asio; @@ -20,12 +20,12 @@ using boost::ut::operator|; using boost::ut::operator/; using boost::ut::expect; using boost::ut::throws; +using boost::ut::fatal; using tfc::snitch::error; using tfc::snitch::info; using tfc::snitch::warning; using tfc::snitch::warning_latched; using tfc::snitch::warning_resettable; -using tfc::snitch::error; using tfc::snitch::detail::dbus_client; using tfc::themis::alarm_database; using tfc::themis::interface; @@ -51,6 +51,13 @@ struct test_setup { } }; +struct formattable_type { + constexpr operator std::string_view() const noexcept { return "42"; } +}; +constexpr auto format_as(formattable_type const& val) -> std::string_view { + return std::string_view{ val }; +} + int main(int argc, char** argv) { tfc::base::init(argc, argv); "Test some dbus functions for signature errors and general correctness"_test = [] { @@ -62,7 +69,7 @@ int main(int argc, char** argv) { }); t.ctx.run_for(2ms); expect(t.ran[0]); - t.client.list_alarms([&](const std::error_code& err, std::vector alarms){ + t.client.list_alarms([&](const std::error_code& err, std::vector alarms) { expect(!err) << err.message(); expect(alarms.size() == 1); t.ran[5] = true; @@ -112,14 +119,12 @@ int main(int argc, char** argv) { expect(t.ran[3]); expect(t.ran[4]); }; - "Test if the try_reset signal makes it in this world"_test = []{ + "Test if the try_reset signal makes it in this world"_test = [] { test_setup t; warning_latched<"desc", "details"> w(t.connection, "first_test"); t.ctx.run_for(1ms); - w.on_try_reset([&]() { - t.ran[1] = true; - }); - t.client.try_reset_alarm(w.alarm_id().value(), [&](const std::error_code& err){ + w.on_try_reset([&]() { t.ran[1] = true; }); + t.client.try_reset_alarm(w.alarm_id().value(), [&](const std::error_code& err) { expect(!!err); t.ran[2] = true; }); @@ -134,10 +139,8 @@ int main(int argc, char** argv) { t.ctx.run_for(2ms); expect(t.ran[0]); expect(w.alarm_id().has_value()); - w.on_try_reset([&]() { - t.ran[3] = true; - }); - t.client.try_reset_alarm(w.alarm_id().value(), [&](const std::error_code& err){ + w.on_try_reset([&]() { t.ran[3] = true; }); + t.client.try_reset_alarm(w.alarm_id().value(), [&](const std::error_code& err) { expect(!err) << err.message(); t.ran[4] = true; }); @@ -145,21 +148,15 @@ int main(int argc, char** argv) { expect(t.ran[3]); expect(t.ran[4]); }; - "Test is the try_reset_all signal tickles these fancies"_test = []{ + "Test is the try_reset_all signal tickles these fancies"_test = [] { test_setup t; warning_latched<"desc1", "details"> wl(t.connection, "first_test"); warning_resettable<"desc2", "details"> wr(t.connection, "first_test"); error<"desc3", "details"> e(t.connection, "first_test"); - wl.on_try_reset([&]() { - t.ran[1] = true; - }); - wr.on_try_reset([&]() { - t.ran[2] = true; - }); - e.on_try_reset([&]() { - t.ran[3] = true; - }); - t.client.try_reset_all_alarms([&](const std::error_code& err){ + wl.on_try_reset([&]() { t.ran[1] = true; }); + wr.on_try_reset([&]() { t.ran[2] = true; }); + e.on_try_reset([&]() { t.ran[3] = true; }); + t.client.try_reset_all_alarms([&](const std::error_code& err) { expect(!!err); t.ran[0] = true; }); @@ -173,7 +170,7 @@ int main(int argc, char** argv) { expect(!err) << err.message(); t.ran[4] = true; }); - t.client.try_reset_all_alarms([&](const std::error_code& err){ + t.client.try_reset_all_alarms([&](const std::error_code& err) { expect(!err) << err.message(); t.ran[5] = true; }); @@ -191,7 +188,7 @@ int main(int argc, char** argv) { expect(!err) << err.message(); t.ran[7] = true; }); - t.client.try_reset_all_alarms([&](const std::error_code& err){ + t.client.try_reset_all_alarms([&](const std::error_code& err) { expect(!err) << err.message(); t.ran[8] = true; }); @@ -207,13 +204,40 @@ int main(int argc, char** argv) { "alarm with declared params but no params provided"_test = [] { test_setup t; info<"desc {foo}", "details {bar} {foo}"> i(t.connection, "first_test"); - try { - i.set(); - expect(false) << "Should have thrown"; - } - catch (std::exception&) { - expect(true); - } + expect(throws([&]{ i.set(); })); + }; + + "alarm with wrong named params"_test = [] { + test_setup t; + info<"desc {foo}", "details {bar} {foo}"> i(t.connection, "first_test"); + expect(throws([&]{ i.set(fmt::arg("ababa", 1), fmt::arg("some_other name", 2)); })); + }; + + "alarm with params"_test = [] { + test_setup t; + info<"desc {foo}", "details {bar} {foo}"> i(t.connection, "first_test"); + i.set( + [&](auto err) { + expect(!err) << fmt::format("Received error: {}", err.message()); + t.ran[0] = true; + + t.client.list_activations( + "en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max()), + [&](auto list_err, std::vector const& act) { + expect(!list_err) << list_err.message(); + expect(fatal(act.size() == 1)); + auto const& alarm = act.at(0); + expect(alarm.description == "desc 1337"); + expect(alarm.details == "details 42 1337"); + t.ran[1] = true; + }); + }, + fmt::arg("foo", 1337), fmt::arg("bar", formattable_type{})); + t.ctx.run_for(2ms); + expect(t.ran[0]); + expect(t.ran[1]); }; return 0; From aadff4e704513a04ddceb0dc99cf808aefcea766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Mon, 24 Jun 2024 12:45:15 +0000 Subject: [PATCH 39/42] Added two failing tests --- exes/themis/inc/alarm_database.hpp | 13 ++- exes/themis/tests/themis_integration_test.cpp | 109 ++++++++++++++++++ libs/snitch/inc/public/tfc/snitch/common.hpp | 13 ++- 3 files changed, 123 insertions(+), 12 deletions(-) diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index 51e9e4bbf3..82d6cac9eb 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -78,7 +78,7 @@ CREATE TABLE IF NOT EXISTS AlarmActivations( alarm_id INTEGER NOT NULL, activation_time LONG INTEGER NOT NULL, reset_time LONG INTEGER, - activation_level BOOLEAN NOT NULL, + activation_level SHORT INTEGER NOT NULL, inserted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(alarm_id) REFERENCES Alarms(alarm_id) ); @@ -244,8 +244,8 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; db_ << "BEGIN;"; std::uint64_t activation_id; try { - db_ << fmt::format("INSERT INTO AlarmActivations(alarm_id, activation_time, activation_level) VALUES({},{},1)", - alarm_id, milliseconds_since_epoch(tp)); + db_ << fmt::format("INSERT INTO AlarmActivations(alarm_id, activation_time, activation_level) VALUES({},{},{})", + alarm_id, milliseconds_since_epoch(tp), static_cast(tfc::snitch::api::active_e::active)); activation_id = static_cast(db_.last_insert_rowid()); for (auto& [key, value] : variables) { @@ -263,7 +263,8 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; if (!is_activation_high(activation_id)) { throw dbus_error("Cannot reset an inactive activation"); } - db_ << fmt::format("UPDATE AlarmActivations SET activation_level = 0, reset_time = {} WHERE activation_id = {};", + db_ << fmt::format("UPDATE AlarmActivations SET activation_level = {}, reset_time = {} WHERE activation_id = {};", + static_cast(tfc::snitch::api::active_e::inactive), milliseconds_since_epoch(tp), activation_id); } @@ -308,7 +309,7 @@ WHERE activation_time >= {} AND activation_time <= {})", db_ << populated_query >> [&](std::uint64_t activation_id, snitch::api::alarm_id_t alarm_id, std::int64_t activation_time, std::optional reset_time, - bool activation_level, std::optional primary_details, + std::int64_t activation_level, std::optional primary_details, std::optional primary_description, std::optional backup_details, std::optional backup_description, bool alarm_latching, std::int8_t alarm_level) { @@ -322,7 +323,7 @@ WHERE activation_time >= {} AND activation_time <= {})", if (reset_time.has_value()) { final_reset_time = timepoint_from_milliseconds(reset_time.value()); } - activations.emplace_back(alarm_id, activation_id, description, details, activation_level, + activations.emplace_back(alarm_id, activation_id, description, details, static_cast(activation_level), static_cast(alarm_level), alarm_latching, timepoint_from_milliseconds(activation_time), final_reset_time, in_locale); }; diff --git a/exes/themis/tests/themis_integration_test.cpp b/exes/themis/tests/themis_integration_test.cpp index f2a2a7c428..6c08337e27 100644 --- a/exes/themis/tests/themis_integration_test.cpp +++ b/exes/themis/tests/themis_integration_test.cpp @@ -30,6 +30,32 @@ using tfc::snitch::detail::dbus_client; using tfc::themis::alarm_database; using tfc::themis::interface; +struct test_setup_s { + asio::io_context ctx; + alarm_database db; + std::shared_ptr connection; + interface face; + std::thread t; + test_setup_s() + : db(true), connection{ std::make_shared(ctx, tfc::dbus::sd_bus_open_system()) }, + face(connection, db), t([&] { ctx.run(); }) {} + ~test_setup_s() { + ctx.stop(); + t.join(); + } +}; + +struct test_setup_c { + asio::io_context ctx; + std::shared_ptr connection; + std::array ran{}; + std::array ids{}; + dbus_client client; + test_setup_c() + : connection{ std::make_shared(ctx, tfc::dbus::sd_bus_open_system()) }, + client(connection) {} +}; + struct test_setup { asio::io_context ctx; asio::io_context sctx; @@ -240,5 +266,88 @@ int main(int argc, char** argv) { expect(t.ran[1]); }; + // TODO: The alarm is recreated but its state is not set again. + "Alarm loses database connection set forgotten"_test = []{ + test_setup_s *server = new test_setup_s(); + test_setup_c client; + info<"desc", "details"> i(client.connection, "dead_server_test"); + { + i.set( + [&](auto err) { + expect(!err) << fmt::format("Received error: {}", err.message()); + client.ran[0] = true; + }); + client.ctx.run_for(2ms); + expect(client.ran[0]); + delete server; + } + { + // This servers database is started in ram again. + // should the alarm should be populated and set again. + client.ran[0] = false; // Reset the set callback. Should probably be called again. + client.ctx.run_for(2ms); + test_setup_s *server2 = new test_setup_s(); + client.ctx.run_for(2ms); + expect(client.ran[0]); + client.client.list_alarms([&](const std::error_code& err, std::vector alarms) { + expect(!err) << err.message(); + expect(alarms.size() == 1); + client.ran[1] = true; + }); + client.ctx.run_for(2ms); + expect(client.ran[1]); + client.client.list_activations( + "en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max()), + [&](const std::error_code& err, std::vector act) { + expect(!err) << err.message(); + expect(act.size() == 1); + client.ran[2] = true; + }); + client.ctx.run_for(2ms); + expect(client.ran[2]); + delete server2; + } + }; + + //TODO: An extra alarm could be created that warns of the lost connection. + "An alarm that is lost shall have its activations status set to unknown"_test = []{ + test_setup_s server; + test_setup_c *client = new test_setup_c(); + info<"desc", "details"> i(client->connection, "dead_client_test"); + client->client.list_alarms([&](const std::error_code& err, std::vector alarms) { + expect(!err) << err.message(); + expect(alarms.size() == 1); + client->ran[0] = true; + }); + client->ctx.run_for(2ms); + expect(client->ran[0]); + i.set( + [&](auto err) { + expect(!err) << fmt::format("Received error: {}", err.message()); + client->ran[1] = true; + }); + client->ctx.run_for(2ms); + expect(client->ran[1]); + delete client; + test_setup_c new_client; + new_client.client.list_activations( + "en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, + tfc::themis::alarm_database::timepoint_from_milliseconds(0), + tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max()), + [&](const std::error_code& err, std::vector act) { + expect(!err) << err.message(); + expect(act.size() == 1); + auto const& alarm = act.at(0); + expect(alarm.description == "desc"); + expect(alarm.details == "details"); + expect(alarm.lvl == tfc::snitch::level_e::info); + expect(alarm.active == tfc::snitch::api::active_e::unknown); + new_client.ran[0] = true; + }); + new_client.ctx.run_for(2ms); + expect(new_client.ran[0]); + }; return 0; } diff --git a/libs/snitch/inc/public/tfc/snitch/common.hpp b/libs/snitch/inc/public/tfc/snitch/common.hpp index 91328a9597..ce4b8ac4f9 100644 --- a/libs/snitch/inc/public/tfc/snitch/common.hpp +++ b/libs/snitch/inc/public/tfc/snitch/common.hpp @@ -9,10 +9,10 @@ namespace tfc::snitch { // std::int8_t is not in the dbus spec, so we use std::int16_t instead enum struct level_e : std::int16_t { all = -1, - unknown, - info, - warning, - error, + info = 0, + warning = 1, + error = 2, + unknown = 32767 }; namespace api { @@ -25,7 +25,8 @@ using activation_id_t = alarm_id_t; enum struct active_e : std::int16_t { all = -1, inactive = 0, - active = 1 + active = 1, + unknown = 32767 }; struct alarm { @@ -48,7 +49,7 @@ struct activation { std::uint64_t activation_id; std::string description; std::string details; - bool active{}; + active_e active{ active_e::unknown }; level_e lvl{ level_e::unknown }; bool latching{}; time_point set_timestamp; From 99acdce304f8a835f3f51f27425bcd1290bca927 Mon Sep 17 00:00:00 2001 From: omarhogni Date: Mon, 24 Jun 2024 12:46:49 +0000 Subject: [PATCH 40/42] :art: Committing clang-format changes --- exes/themis/inc/alarm_database.hpp | 22 +-- exes/themis/inc/dbus_interface.hpp | 115 ++++++------- exes/themis/src/main.cpp | 2 +- exes/themis/tests/themis_database_test.cpp | 104 ++++++------ exes/themis/tests/themis_integration_test.cpp | 38 +++-- .../inc/public/tfc/dbus/match_rules.hpp | 15 +- .../inc/public/tfc/dbus/sdbusplus_meta.hpp | 6 +- libs/snitch/inc/public/tfc/snitch.hpp | 29 ++-- libs/snitch/inc/public/tfc/snitch/common.hpp | 49 +++--- .../public/tfc/snitch/details/dbus_client.hpp | 34 +++- .../public/tfc/snitch/details/snitch_impl.hpp | 21 ++- .../public/tfc/snitch/format_extension.hpp | 17 +- libs/snitch/src/dbus_client.cpp | 152 ++++++++++-------- libs/snitch/src/snitch_impl.cpp | 64 ++++---- libs/snitch/tests/snitch_test.cpp | 8 +- 15 files changed, 351 insertions(+), 325 deletions(-) diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index 82d6cac9eb..134bd791c6 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -13,10 +13,10 @@ #include #include +#include #include #include #include -#include namespace tfc::themis { @@ -125,7 +125,7 @@ CREATE TABLE IF NOT EXISTS AlarmVariables( add_alarm_translation(alarm_id, "en", description, details); // Reset the alarm if high on register - if (is_alarm_active(alarm_id)){ + if (is_alarm_active(alarm_id)) { reset_alarm(alarm_id); } db_ << "COMMIT;"; @@ -224,9 +224,7 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; return count; } - [[nodiscard]] auto is_some_alarm_active() -> bool { - return active_alarm_count() > 0; - } + [[nodiscard]] auto is_some_alarm_active() -> bool { return active_alarm_count() > 0; } /** * @brief Set an alarm in the database @@ -245,7 +243,8 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; std::uint64_t activation_id; try { db_ << fmt::format("INSERT INTO AlarmActivations(alarm_id, activation_time, activation_level) VALUES({},{},{})", - alarm_id, milliseconds_since_epoch(tp), static_cast(tfc::snitch::api::active_e::active)); + alarm_id, milliseconds_since_epoch(tp), + static_cast(tfc::snitch::api::active_e::active)); activation_id = static_cast(db_.last_insert_rowid()); for (auto& [key, value] : variables) { @@ -264,8 +263,8 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; throw dbus_error("Cannot reset an inactive activation"); } db_ << fmt::format("UPDATE AlarmActivations SET activation_level = {}, reset_time = {} WHERE activation_id = {};", - static_cast(tfc::snitch::api::active_e::inactive), - milliseconds_since_epoch(tp), activation_id); + static_cast(tfc::snitch::api::active_e::inactive), milliseconds_since_epoch(tp), + activation_id); } [[nodiscard]] auto list_activations(std::string_view locale, @@ -311,8 +310,8 @@ WHERE activation_time >= {} AND activation_time <= {})", std::int64_t activation_time, std::optional reset_time, std::int64_t activation_level, std::optional primary_details, std::optional primary_description, std::optional backup_details, - std::optional backup_description, - bool alarm_latching, std::int8_t alarm_level) { + std::optional backup_description, bool alarm_latching, + std::int8_t alarm_level) { if (!backup_description.has_value() || !backup_details.has_value()) { throw dbus_error("Backup message not found for alarm translation. This should never happen."); } @@ -323,7 +322,8 @@ WHERE activation_time >= {} AND activation_time <= {})", if (reset_time.has_value()) { final_reset_time = timepoint_from_milliseconds(reset_time.value()); } - activations.emplace_back(alarm_id, activation_id, description, details, static_cast(activation_level), + activations.emplace_back(alarm_id, activation_id, description, details, + static_cast(activation_level), static_cast(alarm_level), alarm_latching, timepoint_from_milliseconds(activation_time), final_reset_time, in_locale); }; diff --git a/exes/themis/inc/dbus_interface.hpp b/exes/themis/inc/dbus_interface.hpp index 70f7aa7458..9ca5958a8c 100644 --- a/exes/themis/inc/dbus_interface.hpp +++ b/exes/themis/inc/dbus_interface.hpp @@ -8,13 +8,12 @@ #include #include #include -#include #include #include #include -#include #include - +#include +#include namespace tfc::themis { using namespace tfc::snitch::api::dbus; @@ -27,65 +26,65 @@ class interface { connection_ = connection; object_server_ = std::make_unique(connection_); connection_->request_name(service_name.data()); - interface_ = - object_server_->add_unique_interface(object_path.data(), interface_name.data()); + interface_ = object_server_->add_unique_interface(object_path.data(), interface_name.data()); interface_->register_method(std::string(methods::list_alarms), - [&]() -> std::string { - return glz::write_json(database.list_alarms()); - }); + [&]() -> std::string { return glz::write_json(database.list_alarms()); }); - interface_->register_method(std::string(methods::register_alarm), - [&](const sdbusplus::message_t& msg, std::string tfc_id, const std::string& description, const std::string& details, bool latching, std::underlying_type_t alarm_level) -> std::uint64_t { - // TODO: User supplied alarm_level needs more verification - return database.register_alarm_en(tfc_id, description, details, latching, static_cast(alarm_level)); - }); + interface_->register_method( + std::string(methods::register_alarm), + [&](const sdbusplus::message_t& msg, std::string tfc_id, const std::string& description, const std::string& details, + bool latching, std::underlying_type_t alarm_level) -> std::uint64_t { + // TODO: User supplied alarm_level needs more verification + return database.register_alarm_en(tfc_id, description, details, latching, + static_cast(alarm_level)); + }); - interface_->register_method(std::string(methods::set_alarm), - [&](snitch::api::alarm_id_t alarm_id, const std::unordered_map& args) -> std::uint64_t { - return database.set_alarm(alarm_id, args); - }); + interface_->register_method( + std::string(methods::set_alarm), + [&](snitch::api::alarm_id_t alarm_id, const std::unordered_map& args) -> std::uint64_t { + return database.set_alarm(alarm_id, args); + }); interface_->register_method(std::string(methods::reset_alarm), - [&](snitch::api::alarm_id_t alarm_id) -> void { - database.reset_alarm(alarm_id); - }); - interface_->register_method(std::string(methods::try_reset), - [&](snitch::api::alarm_id_t alarm_id) -> void { - if (database.is_alarm_active(alarm_id)){ - auto message = interface_->new_signal(signals::try_reset.data()); - message.append(alarm_id); - message.signal_send(); - } else { - throw dbus_error("Alarm is not active"); - } - }); - interface_->register_method(std::string(methods::try_reset_all), - [&]() -> void { - if(database.is_some_alarm_active()){ - auto message = interface_->new_signal(signals::try_reset_all.data()); - message.signal_send(); - } else { - throw dbus_error("No alarm is active"); - } - }); - using tfc::snitch::api::active_e; + [&](snitch::api::alarm_id_t alarm_id) -> void { database.reset_alarm(alarm_id); }); + interface_->register_method(std::string(methods::try_reset), [&](snitch::api::alarm_id_t alarm_id) -> void { + if (database.is_alarm_active(alarm_id)) { + auto message = interface_->new_signal(signals::try_reset.data()); + message.append(alarm_id); + message.signal_send(); + } else { + throw dbus_error("Alarm is not active"); + } + }); + interface_->register_method(std::string(methods::try_reset_all), [&]() -> void { + if (database.is_some_alarm_active()) { + auto message = interface_->new_signal(signals::try_reset_all.data()); + message.signal_send(); + } else { + throw dbus_error("No alarm is active"); + } + }); using tfc::snitch::level_e; + using tfc::snitch::api::active_e; interface_->register_method(std::string(methods::list_activations), - [&](const std::string& locale, std::uint64_t start_count, std::uint64_t count, std::underlying_type_t alarm_level, std::underlying_type_t active, int64_t start, int64_t end) -> std::string { - auto cstart = tfc::themis::alarm_database::timepoint_from_milliseconds(start); - auto cend = tfc::themis::alarm_database::timepoint_from_milliseconds(end); - return glz::write_json(database.list_activations(locale, start_count, count, static_cast(alarm_level), static_cast(active), cstart, cend)); - }); + [&](const std::string& locale, std::uint64_t start_count, std::uint64_t count, + std::underlying_type_t alarm_level, std::underlying_type_t active, + int64_t start, int64_t end) -> std::string { + auto cstart = tfc::themis::alarm_database::timepoint_from_milliseconds(start); + auto cend = tfc::themis::alarm_database::timepoint_from_milliseconds(end); + return glz::write_json(database.list_activations( + locale, start_count, count, static_cast(alarm_level), + static_cast(active), cstart, cend)); + }); // Signal alarm_id, current_activation, ack_status interface_->register_signal>(std::string(signals::alarm_activation_changed)); interface_->register_signal(std::string(signals::try_reset)); interface_->register_signal(std::string(signals::try_reset_all)); fmt::println(stderr, "{}", match_rule_); - name_lost_match_ = std::make_unique( - *connection_, match_rule_.data(), std::bind_front(&interface::match_callback, this)); + name_lost_match_ = std::make_unique(*connection_, match_rule_.data(), + std::bind_front(&interface::match_callback, this)); interface_->initialize(); - } private: @@ -93,21 +92,23 @@ class interface { static constexpr std::string_view dbus_name_ = "org.freedesktop.DBus"sv; static constexpr std::string_view dbus_path_ = "/org/freedesktop/DBus"sv; static constexpr std::string_view name_owner_changed_ = "NameOwnerChanged"sv; - static constexpr std::string_view match_rule_ = tfc::dbus::match::rules::make_match_rule(); + static constexpr std::string_view match_rule_ = tfc::dbus::match::rules:: + make_match_rule(); auto match_callback(sdbusplus::message_t& msg) -> void { std::string name; std::string old_owner; std::string new_owner; msg.read(name, old_owner, new_owner); - if (name == old_owner){ + if (name == old_owner) { // Someone has left the chat. auto iterator = dbus_ids_to_monitor_.find(name); - if (iterator != dbus_ids_to_monitor_.end()){ + if (iterator != dbus_ids_to_monitor_.end()) { // We knew that guy - //TODO: Decide what to do here. - //TODO: Could cleanup the alarm state, and place a warning into the logs that this process is not handling his alarms anymore. - //TODO: Could also just ignore it, and let the alarm state be as it is. - //TODO: There is also the question of alarms that are active when the system is shut down. Who cleans them up? + // TODO: Decide what to do here. + // TODO: Could cleanup the alarm state, and place a warning into the logs that this process is not handling his + // alarms anymore. + // TODO: Could also just ignore it, and let the alarm state be as it is. + // TODO: There is also the question of alarms that are active when the system is shut down. Who cleans them up? fmt::println(stderr, "name: {}, old_owner: {}, new_owner: {}", name, old_owner, new_owner); } } @@ -116,6 +117,6 @@ class interface { std::unique_ptr interface_; std::unique_ptr object_server_; std::unique_ptr name_lost_match_; - std::unordered_map> dbus_ids_to_monitor_; // Alarm members and monitoring parties + std::unordered_map> dbus_ids_to_monitor_; // Alarm members and monitoring parties }; -} // namespace tfc::ipc_ruler +} // namespace tfc::themis diff --git a/exes/themis/src/main.cpp b/exes/themis/src/main.cpp index 864c98a5f6..48cdbd3a27 100644 --- a/exes/themis/src/main.cpp +++ b/exes/themis/src/main.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include #include diff --git a/exes/themis/tests/themis_database_test.cpp b/exes/themis/tests/themis_database_test.cpp index 1fa25f2b75..5a29419792 100644 --- a/exes/themis/tests/themis_database_test.cpp +++ b/exes/themis/tests/themis_database_test.cpp @@ -36,7 +36,7 @@ auto main(int argc, char** argv) -> int { auto alarms = alarm_db.list_alarms(); expect(alarms.size() == 1); expect(alarms.at(0).tfc_id == "tfc_id"); - expect(alarms.at(0).translations.size() == 1); // English is always present + expect(alarms.at(0).translations.size() == 1); // English is always present expect(alarms.at(0).sha1sum == alarm_database::get_sha1("msgshort msg")); expect(alarms.at(0).lvl == tfc::snitch::level_e::info); expect(alarms.at(0).alarm_id == insert_id); @@ -50,7 +50,7 @@ auto main(int argc, char** argv) -> int { "this is is also spanish believe me please"); alarms = alarm_db.list_alarms(); expect(alarms.size() == 1); - expect(alarms.at(0).translations.size() == 2); // english and spanish + expect(alarms.at(0).translations.size() == 2); // english and spanish iterator = alarms.at(0).translations.find("es"); expect(iterator != alarms.at(0).translations.end()); expect(iterator->second.description == "some spanish maybe"); @@ -60,7 +60,7 @@ auto main(int argc, char** argv) -> int { "This is really some icelandic"); alarms = alarm_db.list_alarms(); expect(alarms.size() == 1); - expect(alarms.at(0).translations.size() == 3); // english, spanish and icelandic + expect(alarms.at(0).translations.size() == 3); // english, spanish and icelandic iterator = alarms.at(0).translations.find("is"); expect(iterator != alarms.at(0).translations.end()); expect(iterator->second.description == "Some icelandic really"); @@ -92,12 +92,12 @@ auto main(int argc, char** argv) -> int { expect(activations.size() == 0); // Add some activations - for(int i = 0; i < 10; i++){ + for (int i = 0; i < 10; i++) { auto activation_id = alarm_db.set_alarm(insert_id, {}); alarm_db.reset_alarm(activation_id); } // Insert an invalid activation - expect(throws([&]{[[maybe_unused]] auto _ = alarm_db.set_alarm(999, {}); })); + expect(throws([&] { [[maybe_unused]] auto _ = alarm_db.set_alarm(999, {}); })); // Verify our inserts activations = alarm_db.list_activations( @@ -115,9 +115,8 @@ auto main(int argc, char** argv) -> int { tfc::themis::alarm_database::timepoint_from_milliseconds(0), tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max())); expect(activations.size() == 10) << activations.size(); - }; - "Fallback to en if locale is not available"_test = []{ + "Fallback to en if locale is not available"_test = [] { tfc::themis::alarm_database alarm_db(true); auto insert_id = alarm_db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); alarm_db.add_alarm_translation(alarm_db.list_alarms().at(0).alarm_id, "es", "spanish description", "spanish details"); @@ -140,12 +139,12 @@ auto main(int argc, char** argv) -> int { expect(activations.at(0).details == "spanish details") << activations.at(0).details; expect(activations.at(0).in_requested_locale == true); }; - "String formatting with variables"_test = []{ + "String formatting with variables"_test = [] { tfc::themis::alarm_database alarm_db(true); // Add a variable alarm auto var_alarm_id = alarm_db.register_alarm_en("tfc_id", "{var}", "{var}", false, tfc::snitch::level_e::info); expect(alarm_db.list_alarms().size() == 1); - [[maybe_unused]] auto _ = alarm_db.set_alarm(var_alarm_id, {{ "var", "10.0"}}); + [[maybe_unused]] auto _ = alarm_db.set_alarm(var_alarm_id, { { "var", "10.0" } }); auto activations = alarm_db.list_activations( "en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, tfc::themis::alarm_database::timepoint_from_milliseconds(0), @@ -155,10 +154,12 @@ auto main(int argc, char** argv) -> int { expect(activations.at(0).description == "10.0"); expect(activations.at(0).details == "10.0"); - var_alarm_id = alarm_db.register_alarm_en("tfc_id", "description {var} {var2} {var3}", "details {var} {var2} {var3}", false, tfc::snitch::level_e::warning); + var_alarm_id = alarm_db.register_alarm_en("tfc_id", "description {var} {var2} {var3}", "details {var} {var2} {var3}", + false, tfc::snitch::level_e::warning); expect(alarm_db.list_alarms().size() == 2); - alarm_db.add_alarm_translation(var_alarm_id, "es", "spanish description {var} {var2} {var3}", "spanish details {var} {var2} {var3}"); - _ = alarm_db.set_alarm(var_alarm_id, {{ "var", "10.0"}, {"var2", "20.0"}, {"var3", "30.0"}}); + alarm_db.add_alarm_translation(var_alarm_id, "es", "spanish description {var} {var2} {var3}", + "spanish details {var} {var2} {var3}"); + _ = alarm_db.set_alarm(var_alarm_id, { { "var", "10.0" }, { "var2", "20.0" }, { "var3", "30.0" } }); activations = alarm_db.list_activations( "en", 0, 10000, tfc::snitch::level_e::warning, tfc::snitch::api::active_e::active, tfc::themis::alarm_database::timepoint_from_milliseconds(0), @@ -184,48 +185,50 @@ auto main(int argc, char** argv) -> int { expect(activations.at(0).description == "description 10.0 20.0 30.0") << activations.at(0).description; expect(activations.at(0).details == "details 10.0 20.0 30.0") << activations.at(0).details; }; - "Test the millisecond time storage"_test = []{ - auto min_time = alarm_database::timepoint_from_milliseconds(std::numeric_limits::min()); - auto max_time = alarm_database::timepoint_from_milliseconds(std::numeric_limits::max()); - auto time = alarm_database::timepoint_from_milliseconds(0); - expect(time.time_since_epoch().count() == 0); - time = alarm_database::timepoint_from_milliseconds(1000); - expect(time.time_since_epoch().count() == 1000); - time = alarm_database::timepoint_from_milliseconds(1000000); - expect(time.time_since_epoch().count() == 1000000); - time = alarm_database::timepoint_from_milliseconds(1000000000); - expect(time.time_since_epoch().count() == 1000000000); - time = alarm_database::timepoint_from_milliseconds(1000000000000); - expect(time.time_since_epoch().count() == 1000000000000); - time = alarm_database::timepoint_from_milliseconds(1000000000000000); - expect(time.time_since_epoch().count() == 1000000000000000); + "Test the millisecond time storage"_test = [] { + auto min_time = alarm_database::timepoint_from_milliseconds(std::numeric_limits::min()); + auto max_time = alarm_database::timepoint_from_milliseconds(std::numeric_limits::max()); + auto time = alarm_database::timepoint_from_milliseconds(0); + expect(time.time_since_epoch().count() == 0); + time = alarm_database::timepoint_from_milliseconds(1000); + expect(time.time_since_epoch().count() == 1000); + time = alarm_database::timepoint_from_milliseconds(1000000); + expect(time.time_since_epoch().count() == 1000000); + time = alarm_database::timepoint_from_milliseconds(1000000000); + expect(time.time_since_epoch().count() == 1000000000); + time = alarm_database::timepoint_from_milliseconds(1000000000000); + expect(time.time_since_epoch().count() == 1000000000000); + time = alarm_database::timepoint_from_milliseconds(1000000000000000); + expect(time.time_since_epoch().count() == 1000000000000000); - tfc::themis::alarm_database db(true); - auto alarm_id = db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); - time = alarm_database::timepoint_from_milliseconds(1); - auto activation_id = db.set_alarm(alarm_id, {}, time); - auto activations = db.list_activations("en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::all, min_time, max_time); - expect(activations.size() == 1); - expect(activations.at(0).set_timestamp == time); - expect(!activations.at(0).reset_timestamp.has_value()); - auto reset_time = alarm_database::timepoint_from_milliseconds(2); - db.reset_alarm(activation_id, reset_time); - activations = db.list_activations("en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::all, min_time, max_time); - expect(!db.is_alarm_active(alarm_id)); - expect(activations.size() == 1); - expect(activations.at(0).set_timestamp == time); - expect(activations.at(0).reset_timestamp.has_value()); - expect(activations.at(0).reset_timestamp == reset_time); + tfc::themis::alarm_database db(true); + auto alarm_id = db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); + time = alarm_database::timepoint_from_milliseconds(1); + auto activation_id = db.set_alarm(alarm_id, {}, time); + auto activations = + db.list_activations("en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::all, min_time, max_time); + expect(activations.size() == 1); + expect(activations.at(0).set_timestamp == time); + expect(!activations.at(0).reset_timestamp.has_value()); + auto reset_time = alarm_database::timepoint_from_milliseconds(2); + db.reset_alarm(activation_id, reset_time); + activations = + db.list_activations("en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::all, min_time, max_time); + expect(!db.is_alarm_active(alarm_id)); + expect(activations.size() == 1); + expect(activations.at(0).set_timestamp == time); + expect(activations.at(0).reset_timestamp.has_value()); + expect(activations.at(0).reset_timestamp == reset_time); }; - "You should not be able to set an already set alarm"_test = []{ + "You should not be able to set an already set alarm"_test = [] { tfc::themis::alarm_database db(true); auto alarm_id = db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); auto activation_id = db.set_alarm(alarm_id, {}); - expect(throws([&]{[[maybe_unused]] auto _ = db.set_alarm(alarm_id, {}); })); + expect(throws([&] { [[maybe_unused]] auto _ = db.set_alarm(alarm_id, {}); })); db.reset_alarm(activation_id); - expect(throws([&]{ db.reset_alarm(activation_id, {}); })); + expect(throws([&] { db.reset_alarm(activation_id, {}); })); }; - "Verify alarm id when replacing reinstantiating alarms"_test = []{ + "Verify alarm id when replacing reinstantiating alarms"_test = [] { tfc::themis::alarm_database db(true); auto alarm_id = db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); auto new_alarm_id = db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); @@ -233,10 +236,11 @@ auto main(int argc, char** argv) -> int { expect(alarm_id == new_alarm_id); expect(alarm_id != not_the_same); }; - "re-registering the same alarm should update registered_at"_test = []{ + "re-registering the same alarm should update registered_at"_test = [] { tfc::themis::alarm_database db(true); auto tp = tfc::themis::alarm_database::timepoint_from_milliseconds(0); - [[maybe_unused]] auto alarm_id = db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info, tp); + [[maybe_unused]] auto alarm_id = + db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info, tp); auto alarms = db.list_alarms(); expect(alarms.size() == 1); expect(alarms.at(0).registered_at == tp); @@ -247,7 +251,7 @@ auto main(int argc, char** argv) -> int { expect(alarms.size() == 1); expect(alarms.at(0).registered_at == tp); }; - "registering an alarm should result in a clean-slate"_test = []{ + "registering an alarm should result in a clean-slate"_test = [] { auto db = tfc::themis::alarm_database(true); auto alarm_id = db.register_alarm_en("tfc_id", "description", "details", false, tfc::snitch::level_e::info); [[maybe_unused]] auto activation_id = db.set_alarm(alarm_id, {}); diff --git a/exes/themis/tests/themis_integration_test.cpp b/exes/themis/tests/themis_integration_test.cpp index 6c08337e27..36b818d59d 100644 --- a/exes/themis/tests/themis_integration_test.cpp +++ b/exes/themis/tests/themis_integration_test.cpp @@ -19,8 +19,8 @@ using boost::ut::operator""_test; using boost::ut::operator|; using boost::ut::operator/; using boost::ut::expect; -using boost::ut::throws; using boost::ut::fatal; +using boost::ut::throws; using tfc::snitch::error; using tfc::snitch::info; using tfc::snitch::warning; @@ -230,13 +230,13 @@ int main(int argc, char** argv) { "alarm with declared params but no params provided"_test = [] { test_setup t; info<"desc {foo}", "details {bar} {foo}"> i(t.connection, "first_test"); - expect(throws([&]{ i.set(); })); + expect(throws([&] { i.set(); })); }; "alarm with wrong named params"_test = [] { test_setup t; info<"desc {foo}", "details {bar} {foo}"> i(t.connection, "first_test"); - expect(throws([&]{ i.set(fmt::arg("ababa", 1), fmt::arg("some_other name", 2)); })); + expect(throws([&] { i.set(fmt::arg("ababa", 1), fmt::arg("some_other name", 2)); })); }; "alarm with params"_test = [] { @@ -267,16 +267,15 @@ int main(int argc, char** argv) { }; // TODO: The alarm is recreated but its state is not set again. - "Alarm loses database connection set forgotten"_test = []{ - test_setup_s *server = new test_setup_s(); + "Alarm loses database connection set forgotten"_test = [] { + test_setup_s* server = new test_setup_s(); test_setup_c client; info<"desc", "details"> i(client.connection, "dead_server_test"); { - i.set( - [&](auto err) { - expect(!err) << fmt::format("Received error: {}", err.message()); - client.ran[0] = true; - }); + i.set([&](auto err) { + expect(!err) << fmt::format("Received error: {}", err.message()); + client.ran[0] = true; + }); client.ctx.run_for(2ms); expect(client.ran[0]); delete server; @@ -284,9 +283,9 @@ int main(int argc, char** argv) { { // This servers database is started in ram again. // should the alarm should be populated and set again. - client.ran[0] = false; // Reset the set callback. Should probably be called again. + client.ran[0] = false; // Reset the set callback. Should probably be called again. client.ctx.run_for(2ms); - test_setup_s *server2 = new test_setup_s(); + test_setup_s* server2 = new test_setup_s(); client.ctx.run_for(2ms); expect(client.ran[0]); client.client.list_alarms([&](const std::error_code& err, std::vector alarms) { @@ -311,10 +310,10 @@ int main(int argc, char** argv) { } }; - //TODO: An extra alarm could be created that warns of the lost connection. - "An alarm that is lost shall have its activations status set to unknown"_test = []{ + // TODO: An extra alarm could be created that warns of the lost connection. + "An alarm that is lost shall have its activations status set to unknown"_test = [] { test_setup_s server; - test_setup_c *client = new test_setup_c(); + test_setup_c* client = new test_setup_c(); info<"desc", "details"> i(client->connection, "dead_client_test"); client->client.list_alarms([&](const std::error_code& err, std::vector alarms) { expect(!err) << err.message(); @@ -323,11 +322,10 @@ int main(int argc, char** argv) { }); client->ctx.run_for(2ms); expect(client->ran[0]); - i.set( - [&](auto err) { - expect(!err) << fmt::format("Received error: {}", err.message()); - client->ran[1] = true; - }); + i.set([&](auto err) { + expect(!err) << fmt::format("Received error: {}", err.message()); + client->ran[1] = true; + }); client->ctx.run_for(2ms); expect(client->ran[1]); delete client; diff --git a/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp b/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp index 0a2d82aa70..dc8f6ad2a1 100644 --- a/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp +++ b/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp @@ -90,7 +90,9 @@ template static constexpr std::string_view destination{ detail::filter }; template -static constexpr std::string_view arg{ detail::filter, detail::equal>, arg_in> }; +static constexpr std::string_view arg{ + detail::filter, detail::equal>, arg_in> +}; /// \brief make complete dbus match rule /// \tparam service_name service name @@ -117,12 +119,13 @@ static constexpr std::string_view make_match_rule() { /// ipc_ruler_object_path_c_, tfc::dbus::match::rules::type::signal>()), Reference: /// https://dbus.freedesktop.org/doc/dbus-specification.html template + std::string_view const& interface_name, + std::string_view const& object_path, + std::string_view const& member_name, + std::string_view const& type> static constexpr std::string_view make_match_rule() { - return stx::string_view_join_v, member, interface, path>; + return stx::string_view_join_v, member, interface, + path>; } } // namespace tfc::dbus::match::rules diff --git a/libs/dbus_util/inc/public/tfc/dbus/sdbusplus_meta.hpp b/libs/dbus_util/inc/public/tfc/dbus/sdbusplus_meta.hpp index 29b05c637a..f656ab67a0 100644 --- a/libs/dbus_util/inc/public/tfc/dbus/sdbusplus_meta.hpp +++ b/libs/dbus_util/inc/public/tfc/dbus/sdbusplus_meta.hpp @@ -24,12 +24,10 @@ template struct type_id; template <> -struct type_id : tuple_type_id -{}; +struct type_id : tuple_type_id {}; template <> -struct type_id : tuple_type_id -{}; +struct type_id : tuple_type_id {}; template struct type_id { diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index 5b927a0820..650b5c27f2 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -8,8 +8,8 @@ #include #include -#include #include +#include #include #include #include @@ -42,8 +42,13 @@ class alarm { /// \param unique_id A unique identifier for the alarm within this process /// \param default_args Default fmt::arg values to populate the alarm with, e.g. fmt::arg("index", 1) for tank 1 etc. alarm(std::shared_ptr conn, std::string_view unique_id, named_arg auto&&... default_args) - : impl_{ conn, unique_id, description, details, var.resettable, var.lvl, { std::make_pair(default_args.name, fmt::format("{}", default_args.value))... } } - { + : impl_{ conn, + unique_id, + description, + details, + var.resettable, + var.lvl, + { std::make_pair(default_args.name, fmt::format("{}", default_args.value))... } } { static_assert(detail::check_all_arguments_named(description), "All arguments must be named, e.g. {name}"); static_assert(detail::check_all_arguments_no_format(description), "All arguments may not have format specifiers"); [[maybe_unused]] static constexpr int num_args = sizeof...(default_args); @@ -62,7 +67,7 @@ class alarm { } void set(named_arg auto&&... args) { - set([](auto){}, std::forward(args)...); + set([](auto) {}, std::forward(args)...); } void set(std::function on_set_finished, named_arg auto&&... args) { @@ -73,28 +78,22 @@ class alarm { (store.push_back(args), ...); std::string description_formatted = fmt::vformat(description, store); std::string details_formatted = fmt::vformat(details, store); - impl_.set(description_formatted, details_formatted, { std::make_pair(args.name, fmt::format("{}", args.value))... }, std::move(on_set_finished)); + impl_.set(description_formatted, details_formatted, { std::make_pair(args.name, fmt::format("{}", args.value))... }, + std::move(on_set_finished)); } - void reset(std::function on_reset_finished = [](auto){}) { + void reset(std::function on_reset_finished = [](auto) {}) { impl_.reset(std::move(on_reset_finished)); } - auto alarm_id() const noexcept -> std::optional { - return impl_.alarm_id(); - } - - auto activation_id() const noexcept -> std::optional { - return impl_.activation_id(); - } + auto alarm_id() const noexcept -> std::optional { return impl_.alarm_id(); } + auto activation_id() const noexcept -> std::optional { return impl_.activation_id(); } private: detail::alarm_impl impl_; }; - - template using info = alarm; template diff --git a/libs/snitch/inc/public/tfc/snitch/common.hpp b/libs/snitch/inc/public/tfc/snitch/common.hpp index ce4b8ac4f9..f371e6a132 100644 --- a/libs/snitch/inc/public/tfc/snitch/common.hpp +++ b/libs/snitch/inc/public/tfc/snitch/common.hpp @@ -7,13 +7,7 @@ namespace tfc::snitch { // std::int8_t is not in the dbus spec, so we use std::int16_t instead -enum struct level_e : std::int16_t { - all = -1, - info = 0, - warning = 1, - error = 2, - unknown = 32767 -}; +enum struct level_e : std::int16_t { all = -1, info = 0, warning = 1, error = 2, unknown = 32767 }; namespace api { @@ -22,12 +16,7 @@ using alarm_id_t = std::uint64_t; using activation_id_t = alarm_id_t; // std::int8_t is not in the dbus spec, so we use std::int16_t instead -enum struct active_e : std::int16_t { - all = -1, - inactive = 0, - active = 1, - unknown = 32767 -}; +enum struct active_e : std::int16_t { all = -1, inactive = 0, active = 1, unknown = 32767 }; struct alarm { alarm_id_t alarm_id; @@ -60,24 +49,22 @@ namespace dbus { static constexpr std::string_view service_name = "com.skaginn3x.Alarm"; static constexpr std::string_view interface_name = "com.skaginn3x.Alarm"; static constexpr std::string_view object_path = "/com/skaginn3x/Alarm"; -namespace properties { - -} +namespace properties {} namespace methods { - static constexpr std::string_view register_alarm = "RegisterAlarm"; - static constexpr std::string_view list_alarms = "ListAlarms"; - static constexpr std::string_view list_activations = "ListActivations"; - static constexpr std::string_view set_alarm = "SetAlarm"; - static constexpr std::string_view reset_alarm = "ResetAlarm"; - static constexpr std::string_view try_reset = "TryReset"; - static constexpr std::string_view try_reset_all = "TryResetAll"; -} +static constexpr std::string_view register_alarm = "RegisterAlarm"; +static constexpr std::string_view list_alarms = "ListAlarms"; +static constexpr std::string_view list_activations = "ListActivations"; +static constexpr std::string_view set_alarm = "SetAlarm"; +static constexpr std::string_view reset_alarm = "ResetAlarm"; +static constexpr std::string_view try_reset = "TryReset"; +static constexpr std::string_view try_reset_all = "TryResetAll"; +} // namespace methods namespace signals { - static constexpr std::string_view alarm_activation_changed = "AlarmActivationChanged"; - static constexpr std::string_view try_reset = methods::try_reset; - static constexpr std::string_view try_reset_all = methods::try_reset_all; -} -} -} // namespace api +static constexpr std::string_view alarm_activation_changed = "AlarmActivationChanged"; +static constexpr std::string_view try_reset = methods::try_reset; +static constexpr std::string_view try_reset_all = methods::try_reset_all; +} // namespace signals +} // namespace dbus +} // namespace api -} // namespace tfc::snitch +} // namespace tfc::snitch diff --git a/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp index 6681aa2aa6..5e9f12a262 100644 --- a/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp +++ b/libs/snitch/inc/public/tfc/snitch/details/dbus_client.hpp @@ -1,12 +1,12 @@ #pragma once -#include #include -#include #include +#include +#include -#include #include +#include namespace tfc::snitch::detail { @@ -14,15 +14,32 @@ class dbus_client { public: explicit dbus_client(std::shared_ptr conn); - auto register_alarm(std::string_view tfc_id, std::string_view description, std::string_view details, bool resettable, level_e lvl, std::function token) -> void; + auto register_alarm(std::string_view tfc_id, + std::string_view description, + std::string_view details, + bool resettable, + level_e lvl, + std::function token) -> void; auto list_alarms(std::function)> token) -> void; - auto list_activations(std::string_view locale, std::uint64_t start_count, std::uint64_t count, level_e, api::active_e, api::time_point start, api::time_point end, std::function)> token) -> void; + auto list_activations(std::string_view locale, + std::uint64_t start_count, + std::uint64_t count, + level_e, + api::active_e, + api::time_point start, + api::time_point end, + std::function)> token) -> void; - auto set_alarm(api::alarm_id_t, const std::unordered_map & args, std::function token = [](auto const&, auto){}) -> void; + auto set_alarm( + api::alarm_id_t, + const std::unordered_map& args, + std::function token = [](auto const&, auto) {}) -> void; - auto reset_alarm(api::activation_id_t, std::function token = [](auto const&){}) -> void; + auto reset_alarm( + api::activation_id_t, + std::function token = [](auto const&) {}) -> void; auto try_reset_alarm(api::alarm_id_t, std::function) -> void; @@ -33,6 +50,7 @@ class dbus_client { auto on_try_reset_all_alarms(std::function) -> void; auto on_daemon_alive(std::function) -> void; + private: std::shared_ptr dbus_; std::unique_ptr> try_reset_; @@ -43,4 +61,4 @@ class dbus_client { const std::string object_path_{ api::dbus::object_path }; }; -} // namespace tfc::snitch::detail +} // namespace tfc::snitch::detail diff --git a/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp b/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp index 2301aed1d1..1bb424c525 100644 --- a/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp +++ b/libs/snitch/inc/public/tfc/snitch/details/snitch_impl.hpp @@ -1,15 +1,15 @@ #pragma once -#include #include #include -#include #include +#include +#include #include -#include #include +#include namespace tfc::logger { class logger; @@ -23,7 +23,13 @@ class dbus_client; class alarm_impl { public: - alarm_impl(std::shared_ptr conn, std::string_view unique_id, std::string_view description, std::string_view details, bool resettable, level_e lvl, std::unordered_map&& default_args); + alarm_impl(std::shared_ptr conn, + std::string_view unique_id, + std::string_view description, + std::string_view details, + bool resettable, + level_e lvl, + std::unordered_map&& default_args); alarm_impl(alarm_impl const&) = delete; auto operator=(alarm_impl const&) -> alarm_impl& = delete; alarm_impl(alarm_impl&&) = delete; @@ -32,7 +38,10 @@ class alarm_impl { auto default_values() const noexcept -> auto const& { return default_values_; } void on_try_reset(std::function callback); - void set(std::string_view description_formatted, std::string_view details_formatted, std::unordered_map&& args, std::function&& on_set_finished); + void set(std::string_view description_formatted, + std::string_view details_formatted, + std::unordered_map&& args, + std::function&& on_set_finished); void reset(std::function&& on_reset_finished); auto alarm_id() const noexcept -> std::optional { return alarm_id_; } @@ -57,4 +66,4 @@ class alarm_impl { asio::steady_timer retry_timer_; }; -} // namespace tfc::snitch::detail +} // namespace tfc::snitch::detail diff --git a/libs/snitch/inc/public/tfc/snitch/format_extension.hpp b/libs/snitch/inc/public/tfc/snitch/format_extension.hpp index b8dcd9dbaa..df860e575e 100644 --- a/libs/snitch/inc/public/tfc/snitch/format_extension.hpp +++ b/libs/snitch/inc/public/tfc/snitch/format_extension.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include #include +#include #include @@ -67,18 +67,13 @@ consteval auto arg_count(std::basic_string_view format_str) -> std::size_t return handler.num_args; } - template struct custom_handler_names { constexpr void on_text(const Char*, const Char*) {} - constexpr auto on_arg_id() -> int { - return 0; - } + constexpr auto on_arg_id() -> int { return 0; } - constexpr auto on_arg_id([[maybe_unused]] int id) -> int { - return 0; - } + constexpr auto on_arg_id([[maybe_unused]] int id) -> int { return 0; } constexpr auto on_arg_id(fmt::basic_string_view id) -> int { if (auto it{ std::ranges::find_if(names, [id](const auto& name) { return name == id; }) }; it == names.end()) { @@ -91,9 +86,7 @@ struct custom_handler_names { // todo } - constexpr auto on_format_specs(int id, const Char* begin, const Char*) -> const Char* { - return begin; - } + constexpr auto on_format_specs(int id, const Char* begin, const Char*) -> const Char* { return begin; } constexpr void on_error(const char* msg) { // todo @@ -127,4 +120,4 @@ consteval auto arg_names() -> decltype(auto) { return arg_names_impl::value; } -} // namespace tfc::snitch::detail +} // namespace tfc::snitch::detail diff --git a/libs/snitch/src/dbus_client.cpp b/libs/snitch/src/dbus_client.cpp index 5d8ff89923..06ce019729 100644 --- a/libs/snitch/src/dbus_client.cpp +++ b/libs/snitch/src/dbus_client.cpp @@ -10,8 +10,16 @@ namespace tfc::snitch::detail { -static constexpr auto try_reset_match_{ dbus::match::rules::make_match_rule() }; -static constexpr auto try_reset_all_match_{ dbus::match::rules::make_match_rule() }; +static constexpr auto try_reset_match_{ dbus::match::rules::make_match_rule() }; +static constexpr auto try_reset_all_match_{ dbus::match::rules::make_match_rule() }; using std::string_view_literals::operator""sv; static constexpr std::string_view dbus_service_name = "org.freedesktop.DBus"sv; @@ -19,17 +27,12 @@ static constexpr std::string_view dbus_interface = "org.freedesktop.DBus"sv; static constexpr std::string_view dbus_path = "/org/freedesktop/DBus"sv; static constexpr std::string_view dbus_signal = "NameOwnerChanged"sv; static constexpr std::string_view dbus_arg0 = api::dbus::service_name; -static constexpr std::string_view daemon_alive_match_{ - stx::string_view_join_v, - dbus::match::rules::interface, - dbus::match::rules::path, - dbus::match::rules::member, - dbus::match::rules::arg<0, dbus_arg0> - > -}; - - +static constexpr std::string_view daemon_alive_match_{ stx::string_view_join_v, + dbus::match::rules::interface, + dbus::match::rules::path, + dbus::match::rules::member, + dbus::match::rules::arg<0, dbus_arg0> > }; dbus_client::dbus_client(std::shared_ptr conn) : dbus_{ std::move(conn) } {} auto dbus_client::register_alarm(std::string_view tfc_id, @@ -38,25 +41,29 @@ auto dbus_client::register_alarm(std::string_view tfc_id, bool resettable, level_e lvl, std::function token) -> void { - dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::register_alarm } - // arguments - , std::string{ tfc_id }, std::string{ description }, std::string{ details }, resettable, std::to_underlying(lvl)); + dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, + std::string{ api::dbus::methods::register_alarm } // arguments + , + std::string{ tfc_id }, std::string{ description }, std::string{ details }, resettable, + std::to_underlying(lvl)); } auto dbus_client::list_alarms(std::function)> token) -> void { - dbus_->async_method_call([token_mv = std::move(token)](std::error_code const& err, std::string buffer) { - if (err) { - std::invoke(token_mv, err, std::vector{}); - return; - } - std::vector result{}; - auto parse_err{ glz::read_json(result, buffer) }; - if (parse_err) { - // todo convert to error code - std::invoke(token_mv, std::make_error_code(std::errc::bad_message), std::vector{}); - return; - } - std::invoke(token_mv, err, result); - }, service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::list_alarms }); + dbus_->async_method_call( + [token_mv = std::move(token)](std::error_code const& err, std::string buffer) { + if (err) { + std::invoke(token_mv, err, std::vector{}); + return; + } + std::vector result{}; + auto parse_err{ glz::read_json(result, buffer) }; + if (parse_err) { + // todo convert to error code + std::invoke(token_mv, std::make_error_code(std::errc::bad_message), std::vector{}); + return; + } + std::invoke(token_mv, err, result); + }, + service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::list_alarms }); } auto dbus_client::list_activations(std::string_view locale, std::uint64_t start_count, @@ -66,60 +73,65 @@ auto dbus_client::list_activations(std::string_view locale, api::time_point start, api::time_point end, std::function)> token) -> void { - dbus_->async_method_call([token_mv = std::move(token)](std::error_code const& err, std::string buffer) { - if (err) { - std::invoke(token_mv, err, std::vector{}); - return; - } - std::vector result{}; - auto parse_err{ glz::read_json(result, buffer) }; - if (parse_err) { - // todo convert to error code - std::invoke(token_mv, std::make_error_code(std::errc::bad_message), std::vector{}); - return; - } - std::invoke(token_mv, err, result); - }, service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::list_activations } - // arguments - , std::string{ locale }, start_count, count, std::to_underlying(lvl), std::to_underlying(active), start.time_since_epoch().count(), end.time_since_epoch().count() - ); + dbus_->async_method_call( + [token_mv = std::move(token)](std::error_code const& err, std::string buffer) { + if (err) { + std::invoke(token_mv, err, std::vector{}); + return; + } + std::vector result{}; + auto parse_err{ glz::read_json(result, buffer) }; + if (parse_err) { + // todo convert to error code + std::invoke(token_mv, std::make_error_code(std::errc::bad_message), std::vector{}); + return; + } + std::invoke(token_mv, err, result); + }, + service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::list_activations } // arguments + , + std::string{ locale }, start_count, count, std::to_underlying(lvl), std::to_underlying(active), + start.time_since_epoch().count(), end.time_since_epoch().count()); } -auto dbus_client::set_alarm(api::alarm_id_t id, const std::unordered_map & args, std::function token) - -> void { - dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::set_alarm } - // arguments - , id, args); +auto dbus_client::set_alarm(api::alarm_id_t id, + const std::unordered_map& args, + std::function token) -> void { + dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, + std::string{ api::dbus::methods::set_alarm } // arguments + , + id, args); } auto dbus_client::reset_alarm(api::activation_id_t id, std::function token) -> void { - dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::reset_alarm } - // arguments - , id); + dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, + std::string{ api::dbus::methods::reset_alarm } // arguments + , + id); } auto dbus_client::try_reset_alarm(api::alarm_id_t id, std::function token) -> void { - dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::try_reset } - // arguments - , id); + dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, + std::string{ api::dbus::methods::try_reset } // arguments + , + id); } auto dbus_client::try_reset_all_alarms(std::function token) -> void { dbus_->async_method_call(std::move(token), service_name_, object_path_, interface_name_, std::string{ api::dbus::methods::try_reset_all }); } auto dbus_client::on_try_reset_alarm(std::function token) -> void { - try_reset_ = std::make_unique(*dbus_, try_reset_match_.data(), [token_mv = std::move(token)](sdbusplus::message_t& msg) { - api::alarm_id_t id; - msg.read(id); - std::invoke(token_mv, id); - }); + try_reset_ = std::make_unique(*dbus_, try_reset_match_.data(), + [token_mv = std::move(token)](sdbusplus::message_t& msg) { + api::alarm_id_t id; + msg.read(id); + std::invoke(token_mv, id); + }); } auto dbus_client::on_try_reset_all_alarms(std::function token) -> void { - try_reset_all_ = std::make_unique(*dbus_, try_reset_all_match_.data(), [token_mv = std::move(token)](sdbusplus::message_t&) { - std::invoke(token_mv); - }); + try_reset_all_ = std::make_unique( + *dbus_, try_reset_all_match_.data(), [token_mv = std::move(token)](sdbusplus::message_t&) { std::invoke(token_mv); }); } auto dbus_client::on_daemon_alive(std::function token) -> void { - daemon_alive_ = std::make_unique(*dbus_, daemon_alive_match_.data(), [token_mv = std::move(token)](sdbusplus::message_t&) { - std::invoke(token_mv); - }); + daemon_alive_ = std::make_unique( + *dbus_, daemon_alive_match_.data(), [token_mv = std::move(token)](sdbusplus::message_t&) { std::invoke(token_mv); }); } -} // namespace tfc::snitch::detail +} // namespace tfc::snitch::detail diff --git a/libs/snitch/src/snitch_impl.cpp b/libs/snitch/src/snitch_impl.cpp index 319b64a320..527d065961 100644 --- a/libs/snitch/src/snitch_impl.cpp +++ b/libs/snitch/src/snitch_impl.cpp @@ -2,9 +2,9 @@ #include #include +#include #include #include -#include namespace tfc::snitch::detail { @@ -15,11 +15,13 @@ alarm_impl::alarm_impl(std::shared_ptr conn, bool resettable, level_e lvl, std::unordered_map&& default_args) - : given_id_{ unique_id }, description_{ description }, details_{ details }, resettable_{ resettable }, lvl_{ lvl }, tfc_id_{ fmt::format("{}.{}.{}", base::get_exe_name(), base::get_proc_name(), given_id_) }, default_values_{ std::move(default_args) }, - conn_{ std::move(conn) }, dbus_client_{ std::make_unique(conn_) }, + : given_id_{ unique_id }, description_{ description }, details_{ details }, resettable_{ resettable }, lvl_{ lvl }, + tfc_id_{ fmt::format("{}.{}.{}", base::get_exe_name(), base::get_proc_name(), given_id_) }, + default_values_{ std::move(default_args) }, conn_{ std::move(conn) }, + dbus_client_{ std::make_unique(conn_) }, logger_{ std::make_unique(fmt::format("snitch.{}", given_id_)) }, retry_timer_{ conn_->get_io_context() } { - dbus_client_->on_daemon_alive([this]{ this->on_daemon_alive(); }); + dbus_client_->on_daemon_alive([this] { this->on_daemon_alive(); }); register_alarm(); } @@ -36,7 +38,10 @@ void alarm_impl::on_try_reset(std::function callback) { }); } -void alarm_impl::set(std::string_view description_formatted, std::string_view details_formatted, std::unordered_map&& args, std::function&& on_set_finished) { +void alarm_impl::set(std::string_view description_formatted, + std::string_view details_formatted, + std::unordered_map&& args, + std::function&& on_set_finished) { if (activation_id_) { logger_->info("alarm already active with id: {}, not doing anything", activation_id_.value()); return; @@ -51,35 +56,36 @@ void alarm_impl::set(std::string_view description_formatted, std::string_view de set(std::move(params), std::move(on_set_finished)); } -void alarm_impl::set(std::unordered_map&& params, std::function&& on_set_finished) { +void alarm_impl::set(std::unordered_map&& params, + std::function&& on_set_finished) { auto const now = decltype(retry_timer_)::clock_type::now(); if (now > retry_timer_.expiry()) { retry_timer_.cancel(); } if (alarm_id_) { - dbus_client_->set_alarm(alarm_id_.value(), params, [this, cb = std::move(on_set_finished)](std::error_code const& ec, api::activation_id_t id) { - if (ec) { - logger_->error("Failed to set alarm: {}", ec.message()); - // todo: should we call set here? - } - else { - activation_id_ = id; - } - std::invoke(cb, ec); - }); + dbus_client_->set_alarm(alarm_id_.value(), params, + [this, cb = std::move(on_set_finished)](std::error_code const& ec, api::activation_id_t id) { + if (ec) { + logger_->error("Failed to set alarm: {}", ec.message()); + // todo: should we call set here? + } else { + activation_id_ = id; + } + std::invoke(cb, ec); + }); } else { retry_timer_.expires_after(std::chrono::seconds(1)); - retry_timer_.async_wait([this, params_mv = std::move(params), cb = std::move(on_set_finished)](std::error_code const& ec) mutable { - if (ec == std::errc::operation_canceled && alarm_id_.has_value()) { - // timer was cancelled, but alarm id was set in the meantime - } - else if (ec) { - logger_->info("Retry set timer failed: {}", ec.message()); - return; - } - logger_->debug("Retrying set alarm"); - set(std::move(params_mv), std::move(cb)); - }); + retry_timer_.async_wait( + [this, params_mv = std::move(params), cb = std::move(on_set_finished)](std::error_code const& ec) mutable { + if (ec == std::errc::operation_canceled && alarm_id_.has_value()) { + // timer was cancelled, but alarm id was set in the meantime + } else if (ec) { + logger_->info("Retry set timer failed: {}", ec.message()); + return; + } + logger_->debug("Retrying set alarm"); + set(std::move(params_mv), std::move(cb)); + }); } } @@ -91,15 +97,13 @@ void alarm_impl::reset(std::function&& on_reset_finished) dbus_client_->reset_alarm(activation_id_.value(), [this, cb = std::move(on_reset_finished)](std::error_code const& ec) { if (ec) { logger_->error("Failed to reset alarm: {}", ec.message()); - } - else { + } else { activation_id_.reset(); } std::invoke(cb, ec); }); } - void alarm_impl::register_alarm() { dbus_client_->register_alarm(tfc_id_, description_, details_, resettable_, lvl_, [this](std::error_code const& ec, api::alarm_id_t id) { diff --git a/libs/snitch/tests/snitch_test.cpp b/libs/snitch/tests/snitch_test.cpp index 99e0cfe0f0..f34eea4d4e 100644 --- a/libs/snitch/tests/snitch_test.cpp +++ b/libs/snitch/tests/snitch_test.cpp @@ -5,9 +5,9 @@ #include #include +#include #include #include -#include using std::string_view_literals::operator""sv; namespace asio = boost::asio; @@ -51,10 +51,10 @@ auto main(int argc, char** argv) -> int { "snitches"_test = [] { asio::io_context ctx; auto connection = std::make_shared(ctx, tfc::dbus::sd_bus_open_system()); - tfc::snitch::info<"short desc {name}", "long desc {name} {index}"> tank(connection, "unique_id", fmt::arg("name", "hello"), fmt::arg("index", 42)); - tank.set([](auto){}); + tfc::snitch::info<"short desc {name}", "long desc {name} {index}"> tank( + connection, "unique_id", fmt::arg("name", "hello"), fmt::arg("index", 42)); + tank.set([](auto) {}); // warn.on_ack([]{}); - }; } From a193dbb6d7c7d7c8054eeb245a4b2e8353e81216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93mar=20H=C3=B6gni=20Gu=C3=B0marsson?= Date: Mon, 24 Jun 2024 13:51:06 +0000 Subject: [PATCH 41/42] Added functionality where services disapeared get their alarms marked as unknown --- exes/operations/inc/state_machine_owner.hpp | 4 +- .../tests/operation_mode_integration_test.cpp | 4 +- exes/themis/inc/alarm_database.hpp | 47 ++++++++++++------- exes/themis/inc/dbus_interface.hpp | 45 +++++++++++------- exes/themis/tests/themis_integration_test.cpp | 13 +++-- .../tfc/confman/detail/config_dbus_client.hpp | 4 +- .../inc/public/tfc/dbus/match_rules.hpp | 4 +- .../inc/public/tfc/dbus/sml_interface.hpp | 4 +- .../tests/examples/example_sml_interface.cpp | 4 +- .../example/liboperation_mode_example.cpp | 4 +- libs/snitch/inc/public/tfc/snitch.hpp | 2 +- 11 files changed, 76 insertions(+), 59 deletions(-) diff --git a/exes/operations/inc/state_machine_owner.hpp b/exes/operations/inc/state_machine_owner.hpp index 3a59a31f36..750f6aea0d 100644 --- a/exes/operations/inc/state_machine_owner.hpp +++ b/exes/operations/inc/state_machine_owner.hpp @@ -144,9 +144,7 @@ class state_machine_owner { tfc::confman::config config_{ dbus_, "state_machine", detail::storage{ .startup_time = std::chrono::milliseconds{ 0 }, .stopping_time = std::chrono::milliseconds{ 0 } } }; - tfc::dbus::sml::interface sml_interface_ { - dbus_, "OperationState" - }; + tfc::dbus::sml::interface sml_interface_{ dbus_, "OperationState" }; using state_machine_t = sml_t, boost::sml::logger>; std::shared_ptr states_; }; diff --git a/exes/operations/tests/operation_mode_integration_test.cpp b/exes/operations/tests/operation_mode_integration_test.cpp index 3bc8347c0f..b0ca00e7bc 100644 --- a/exes/operations/tests/operation_mode_integration_test.cpp +++ b/exes/operations/tests/operation_mode_integration_test.cpp @@ -19,9 +19,7 @@ using boost::ut::operator""_test; struct operation_mode_test { asio::io_context ctx{}; tfc::app_operation_mode app{ ctx }; - tfc::operation::interface lib { - ctx, "operation", tfc::dbus::make_dbus_process_name() - }; + tfc::operation::interface lib{ ctx, "operation", tfc::dbus::make_dbus_process_name() }; ~operation_mode_test() { std::error_code code; std::filesystem::remove_all(tfc::base::make_config_file_name("", ""), code); diff --git a/exes/themis/inc/alarm_database.hpp b/exes/themis/inc/alarm_database.hpp index 134bd791c6..a6a8c48320 100644 --- a/exes/themis/inc/alarm_database.hpp +++ b/exes/themis/inc/alarm_database.hpp @@ -115,7 +115,7 @@ CREATE TABLE IF NOT EXISTS AlarmVariables( db_ << fmt::format( "INSERT INTO Alarms(tfc_id, sha1sum, alarm_level, alarm_latching, registered_at) VALUES('{}','{}',{}, {}, {}) ON " "CONFLICT (tfc_id, sha1sum) DO UPDATE SET registered_at={};", - tfc_id, sha1_ascii, static_cast(alarm_level), latching ? 1 : 0, ms_count_registered_at, + tfc_id, sha1_ascii, std::to_underlying(alarm_level), latching ? 1 : 0, ms_count_registered_at, ms_count_registered_at); auto insert_id = db_.last_insert_rowid(); if (insert_id < 0) { @@ -156,8 +156,9 @@ LEFT JOIN AlarmTranslations ON Alarms.sha1sum = AlarmTranslations.sha1sum; )"; // Accept the second table paramters as unique ptr's as the values can be null, and we want to preserve that - db_ << query >> [&](snitch::api::alarm_id_t alarm_id, std::string tfc_id, std::string sha1sum, std::uint8_t alarm_level, - bool alarm_latching, std::optional registered_at, std::optional locale, + db_ << query >> [&](snitch::api::alarm_id_t alarm_id, std::string tfc_id, std::string sha1sum, + std::underlying_type_t alarm_level, bool alarm_latching, + std::optional registered_at, std::optional locale, std::optional translated_description, std::optional translated_details) { auto iterator = alarms.find(alarm_id); if (iterator == alarms.end()) { @@ -243,9 +244,8 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; std::uint64_t activation_id; try { db_ << fmt::format("INSERT INTO AlarmActivations(alarm_id, activation_time, activation_level) VALUES({},{},{})", - alarm_id, milliseconds_since_epoch(tp), - static_cast(tfc::snitch::api::active_e::active)); - activation_id = static_cast(db_.last_insert_rowid()); + alarm_id, milliseconds_since_epoch(tp), std::to_underlying(tfc::snitch::api::active_e::active)); + activation_id = static_cast(db_.last_insert_rowid()); for (auto& [key, value] : variables) { db_ << fmt::format("INSERT INTO AlarmVariables(activation_id, variable_key, variable_value) VALUES({},'{}','{}');", @@ -263,9 +263,24 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum; throw dbus_error("Cannot reset an inactive activation"); } db_ << fmt::format("UPDATE AlarmActivations SET activation_level = {}, reset_time = {} WHERE activation_id = {};", - static_cast(tfc::snitch::api::active_e::inactive), milliseconds_since_epoch(tp), + std::to_underlying(tfc::snitch::api::active_e::inactive), milliseconds_since_epoch(tp), activation_id); } + auto set_activation_status(snitch::api::alarm_id_t activation_id, tfc::snitch::api::active_e activation) -> void { + if (!is_activation_high(activation_id)) { + throw dbus_error("Cannot reset an inactive activation"); + } + db_ << fmt::format("UPDATE AlarmActivations SET activation_level = {} WHERE activation_id = {};", + std::to_underlying(activation), activation_id); + } + + auto get_activation_id_for_active_alarm(snitch::api::alarm_id_t alarm_id) -> std::optional { + std::optional activation_id = std::nullopt; + db_ << fmt::format("SELECT activation_id FROM AlarmActivations WHERE alarm_id = {} AND activation_level = {};", alarm_id, + std::to_underlying(tfc::snitch::api::active_e::active)) >> + [&](std::uint64_t id) { activation_id = id; }; + return activation_id; + } [[nodiscard]] auto list_activations(std::string_view locale, std::uint64_t start_count, @@ -297,21 +312,21 @@ LEFT OUTER JOIN AlarmTranslations as backup_text on (Alarms.alarm_id = backup_te WHERE activation_time >= {} AND activation_time <= {})", locale, milliseconds_since_epoch(start), milliseconds_since_epoch(end)); if (level != tfc::snitch::level_e::all) { - populated_query += fmt::format(" AND alarm_level = {}", static_cast(level)); + populated_query += fmt::format(" AND alarm_level = {}", std::to_underlying(level)); } if (active != tfc::snitch::api::active_e::all) { - populated_query += fmt::format(" AND activation_level = {}", static_cast(active)); + populated_query += fmt::format(" AND activation_level = {}", std::to_underlying(active)); } populated_query += fmt::format(" LIMIT {} OFFSET {};", count, start_count); std::vector activations; - db_ << populated_query >> [&](std::uint64_t activation_id, snitch::api::alarm_id_t alarm_id, + db_ << populated_query >> [&](snitch::api::activation_id_t activation_id, snitch::api::alarm_id_t alarm_id, std::int64_t activation_time, std::optional reset_time, - std::int64_t activation_level, std::optional primary_details, - std::optional primary_description, std::optional backup_details, - std::optional backup_description, bool alarm_latching, - std::int8_t alarm_level) { + std::underlying_type_t activation_level, + std::optional primary_details, std::optional primary_description, + std::optional backup_details, std::optional backup_description, + bool alarm_latching, std::underlying_type_t alarm_level) { if (!backup_description.has_value() || !backup_details.has_value()) { throw dbus_error("Backup message not found for alarm translation. This should never happen."); } @@ -323,8 +338,8 @@ WHERE activation_time >= {} AND activation_time <= {})", final_reset_time = timepoint_from_milliseconds(reset_time.value()); } activations.emplace_back(alarm_id, activation_id, description, details, - static_cast(activation_level), - static_cast(alarm_level), alarm_latching, + static_cast(activation_level), + static_cast(alarm_level), alarm_latching, timepoint_from_milliseconds(activation_time), final_reset_time, in_locale); }; for (auto& activation : activations) { diff --git a/exes/themis/inc/dbus_interface.hpp b/exes/themis/inc/dbus_interface.hpp index 9ca5958a8c..1dbd7bcde8 100644 --- a/exes/themis/inc/dbus_interface.hpp +++ b/exes/themis/inc/dbus_interface.hpp @@ -22,7 +22,8 @@ using dbus_error = tfc::dbus::exception::runtime; class interface { public: - explicit interface(std::shared_ptr connection, tfc::themis::alarm_database& database) { + explicit interface(std::shared_ptr connection, tfc::themis::alarm_database& database) + : database_(database) { connection_ = connection; object_server_ = std::make_unique(connection_); connection_->request_name(service_name.data()); @@ -35,9 +36,14 @@ class interface { std::string(methods::register_alarm), [&](const sdbusplus::message_t& msg, std::string tfc_id, const std::string& description, const std::string& details, bool latching, std::underlying_type_t alarm_level) -> std::uint64_t { - // TODO: User supplied alarm_level needs more verification - return database.register_alarm_en(tfc_id, description, details, latching, - static_cast(alarm_level)); + std::string sender = msg.get_sender(); + if (dbus_ids_to_monitor_.find(sender) == dbus_ids_to_monitor_.end()) { + dbus_ids_to_monitor_[sender] = {}; + } + auto alarm_id = database.register_alarm_en(tfc_id, description, details, latching, + static_cast(alarm_level)); + dbus_ids_to_monitor_[sender].push_back(alarm_id); + return alarm_id; }); interface_->register_method( @@ -81,7 +87,6 @@ class interface { interface_->register_signal>(std::string(signals::alarm_activation_changed)); interface_->register_signal(std::string(signals::try_reset)); interface_->register_signal(std::string(signals::try_reset_all)); - fmt::println(stderr, "{}", match_rule_); name_lost_match_ = std::make_unique(*connection_, match_rule_.data(), std::bind_front(&interface::match_callback, this)); interface_->initialize(); @@ -95,21 +100,23 @@ class interface { static constexpr std::string_view match_rule_ = tfc::dbus::match::rules:: make_match_rule(); auto match_callback(sdbusplus::message_t& msg) -> void { - std::string name; - std::string old_owner; - std::string new_owner; - msg.read(name, old_owner, new_owner); + std::tuple container; + sdbusplus::utility::read_into_tuple(container, msg); + auto [name, old_owner, new_owner] = container; if (name == old_owner) { - // Someone has left the chat. auto iterator = dbus_ids_to_monitor_.find(name); if (iterator != dbus_ids_to_monitor_.end()) { - // We knew that guy - // TODO: Decide what to do here. - // TODO: Could cleanup the alarm state, and place a warning into the logs that this process is not handling his - // alarms anymore. - // TODO: Could also just ignore it, and let the alarm state be as it is. - // TODO: There is also the question of alarms that are active when the system is shut down. Who cleans them up? - fmt::println(stderr, "name: {}, old_owner: {}, new_owner: {}", name, old_owner, new_owner); + // Someone who reported an alarm to us has gone away. Set all their active alarms to unknown + // status + for (auto& [peer, alarm_vec] : dbus_ids_to_monitor_) { + for (auto& alarm_id : alarm_vec) { + auto activation = database_.get_activation_id_for_active_alarm(alarm_id); + if (activation.has_value()) { + database_.set_activation_status(activation.value(), tfc::snitch::api::active_e::unknown); + } + } + } + dbus_ids_to_monitor_.erase(iterator); } } } @@ -117,6 +124,8 @@ class interface { std::unique_ptr interface_; std::unique_ptr object_server_; std::unique_ptr name_lost_match_; - std::unordered_map> dbus_ids_to_monitor_; // Alarm members and monitoring parties + std::unordered_map> + dbus_ids_to_monitor_; // Alarm members and monitoring parties + alarm_database& database_; }; } // namespace tfc::themis diff --git a/exes/themis/tests/themis_integration_test.cpp b/exes/themis/tests/themis_integration_test.cpp index 36b818d59d..1da59763b8 100644 --- a/exes/themis/tests/themis_integration_test.cpp +++ b/exes/themis/tests/themis_integration_test.cpp @@ -54,6 +54,10 @@ struct test_setup_c { test_setup_c() : connection{ std::make_shared(ctx, tfc::dbus::sd_bus_open_system()) }, client(connection) {} + ~test_setup_c() { + expect(connection.use_count() == 2); + connection->close(); + } }; struct test_setup { @@ -314,7 +318,8 @@ int main(int argc, char** argv) { "An alarm that is lost shall have its activations status set to unknown"_test = [] { test_setup_s server; test_setup_c* client = new test_setup_c(); - info<"desc", "details"> i(client->connection, "dead_client_test"); + info<"desc", "details">* i = new info<"desc", "details">(client->connection, "dead_client_test"); + client->connection->request_name("com.skaginn3x.test_this_name"); client->client.list_alarms([&](const std::error_code& err, std::vector alarms) { expect(!err) << err.message(); expect(alarms.size() == 1); @@ -322,16 +327,18 @@ int main(int argc, char** argv) { }); client->ctx.run_for(2ms); expect(client->ran[0]); - i.set([&](auto err) { + i->set([&](auto err) { expect(!err) << fmt::format("Received error: {}", err.message()); client->ran[1] = true; }); client->ctx.run_for(2ms); expect(client->ran[1]); + delete i; delete client; + fmt::println(stderr, "Client has been killed"); test_setup_c new_client; new_client.client.list_activations( - "en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::active, + "en", 0, 10000, tfc::snitch::level_e::info, tfc::snitch::api::active_e::unknown, tfc::themis::alarm_database::timepoint_from_milliseconds(0), tfc::themis::alarm_database::timepoint_from_milliseconds(std::numeric_limits::max()), [&](const std::error_code& err, std::vector act) { diff --git a/libs/confman/inc/public/tfc/confman/detail/config_dbus_client.hpp b/libs/confman/inc/public/tfc/confman/detail/config_dbus_client.hpp index 1e6161223e..6d92b19554 100644 --- a/libs/confman/inc/public/tfc/confman/detail/config_dbus_client.hpp +++ b/libs/confman/inc/public/tfc/confman/detail/config_dbus_client.hpp @@ -21,9 +21,7 @@ namespace dbus { static constexpr std::string_view property_value_name{ "Value" }; static constexpr std::string_view property_schema_name{ "Schema" }; static constexpr std::string_view intent_name{ "Config" }; -static constexpr std::string_view interface { - tfc::dbus::const_dbus_name -}; +static constexpr std::string_view interface{ tfc::dbus::const_dbus_name }; } // namespace dbus class config_dbus_client { diff --git a/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp b/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp index dc8f6ad2a1..2fc3031011 100644 --- a/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp +++ b/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp @@ -50,9 +50,7 @@ static constexpr std::string_view sender{ detail::filter -static constexpr std::string_view interface { - detail::filter -}; +static constexpr std::string_view interface{ detail::filter }; /// \brief make dbus `member` match rule /// \tparam member_in filter value diff --git a/libs/dbus_util/inc/public/tfc/dbus/sml_interface.hpp b/libs/dbus_util/inc/public/tfc/dbus/sml_interface.hpp index bdcc57e3fb..6d2dbdeb42 100644 --- a/libs/dbus_util/inc/public/tfc/dbus/sml_interface.hpp +++ b/libs/dbus_util/inc/public/tfc/dbus/sml_interface.hpp @@ -18,9 +18,7 @@ namespace tfc::dbus::sml { namespace tags { static constexpr std::string_view state_machine{ "StateMachine" }; -static constexpr std::string_view interface { - const_dbus_name -}; +static constexpr std::string_view interface{ const_dbus_name }; } // namespace tags namespace detail { diff --git a/libs/dbus_util/tests/examples/example_sml_interface.cpp b/libs/dbus_util/tests/examples/example_sml_interface.cpp index 90c420c183..cb77745c04 100644 --- a/libs/dbus_util/tests/examples/example_sml_interface.cpp +++ b/libs/dbus_util/tests/examples/example_sml_interface.cpp @@ -34,9 +34,7 @@ auto main(int argc, char** argv) -> int { // Raw dbus connection, ipc_client also has a dbus connection which can be used through ipc_client.connection() auto const dbus{ std::make_shared(ctx) }; - tfc::dbus::sml::interface sml_interface { - dbus, "unique_key" - }; + tfc::dbus::sml::interface sml_interface{ dbus, "unique_key" }; using state_machine_t = boost::sml::sm >; diff --git a/libs/operation_mode/testing/example/liboperation_mode_example.cpp b/libs/operation_mode/testing/example/liboperation_mode_example.cpp index 231437fe21..b3b5b114a3 100644 --- a/libs/operation_mode/testing/example/liboperation_mode_example.cpp +++ b/libs/operation_mode/testing/example/liboperation_mode_example.cpp @@ -12,9 +12,7 @@ auto main(int argc, char** argv) -> int { asio::io_context ctx{}; - tfc::operation::interface mode { - ctx - }; + tfc::operation::interface mode{ ctx }; mode.on_leave(tfc::operation::mode_e::stopped, [](tfc::operation::mode_e new_mode, tfc::operation::mode_e old_mode) { fmt::print("Leaving {} and going to: {}", enum_name(old_mode), enum_name(new_mode)); diff --git a/libs/snitch/inc/public/tfc/snitch.hpp b/libs/snitch/inc/public/tfc/snitch.hpp index 650b5c27f2..dcc7e6b5cb 100644 --- a/libs/snitch/inc/public/tfc/snitch.hpp +++ b/libs/snitch/inc/public/tfc/snitch.hpp @@ -42,7 +42,7 @@ class alarm { /// \param unique_id A unique identifier for the alarm within this process /// \param default_args Default fmt::arg values to populate the alarm with, e.g. fmt::arg("index", 1) for tank 1 etc. alarm(std::shared_ptr conn, std::string_view unique_id, named_arg auto&&... default_args) - : impl_{ conn, + : impl_{ std::move(conn), unique_id, description, details, From 4eae243d5ffed32d6e78df9adb70734783dea13f Mon Sep 17 00:00:00 2001 From: omarhogni Date: Mon, 24 Jun 2024 13:52:53 +0000 Subject: [PATCH 42/42] :art: Committing clang-format changes --- exes/operations/inc/state_machine_owner.hpp | 4 +++- exes/operations/tests/operation_mode_integration_test.cpp | 4 +++- .../inc/public/tfc/confman/detail/config_dbus_client.hpp | 4 +++- libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp | 4 +++- libs/dbus_util/inc/public/tfc/dbus/sml_interface.hpp | 4 +++- libs/dbus_util/tests/examples/example_sml_interface.cpp | 4 +++- .../testing/example/liboperation_mode_example.cpp | 4 +++- 7 files changed, 21 insertions(+), 7 deletions(-) diff --git a/exes/operations/inc/state_machine_owner.hpp b/exes/operations/inc/state_machine_owner.hpp index 750f6aea0d..3a59a31f36 100644 --- a/exes/operations/inc/state_machine_owner.hpp +++ b/exes/operations/inc/state_machine_owner.hpp @@ -144,7 +144,9 @@ class state_machine_owner { tfc::confman::config config_{ dbus_, "state_machine", detail::storage{ .startup_time = std::chrono::milliseconds{ 0 }, .stopping_time = std::chrono::milliseconds{ 0 } } }; - tfc::dbus::sml::interface sml_interface_{ dbus_, "OperationState" }; + tfc::dbus::sml::interface sml_interface_ { + dbus_, "OperationState" + }; using state_machine_t = sml_t, boost::sml::logger>; std::shared_ptr states_; }; diff --git a/exes/operations/tests/operation_mode_integration_test.cpp b/exes/operations/tests/operation_mode_integration_test.cpp index b0ca00e7bc..3bc8347c0f 100644 --- a/exes/operations/tests/operation_mode_integration_test.cpp +++ b/exes/operations/tests/operation_mode_integration_test.cpp @@ -19,7 +19,9 @@ using boost::ut::operator""_test; struct operation_mode_test { asio::io_context ctx{}; tfc::app_operation_mode app{ ctx }; - tfc::operation::interface lib{ ctx, "operation", tfc::dbus::make_dbus_process_name() }; + tfc::operation::interface lib { + ctx, "operation", tfc::dbus::make_dbus_process_name() + }; ~operation_mode_test() { std::error_code code; std::filesystem::remove_all(tfc::base::make_config_file_name("", ""), code); diff --git a/libs/confman/inc/public/tfc/confman/detail/config_dbus_client.hpp b/libs/confman/inc/public/tfc/confman/detail/config_dbus_client.hpp index 6d92b19554..1e6161223e 100644 --- a/libs/confman/inc/public/tfc/confman/detail/config_dbus_client.hpp +++ b/libs/confman/inc/public/tfc/confman/detail/config_dbus_client.hpp @@ -21,7 +21,9 @@ namespace dbus { static constexpr std::string_view property_value_name{ "Value" }; static constexpr std::string_view property_schema_name{ "Schema" }; static constexpr std::string_view intent_name{ "Config" }; -static constexpr std::string_view interface{ tfc::dbus::const_dbus_name }; +static constexpr std::string_view interface { + tfc::dbus::const_dbus_name +}; } // namespace dbus class config_dbus_client { diff --git a/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp b/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp index 2fc3031011..dc8f6ad2a1 100644 --- a/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp +++ b/libs/dbus_util/inc/public/tfc/dbus/match_rules.hpp @@ -50,7 +50,9 @@ static constexpr std::string_view sender{ detail::filter -static constexpr std::string_view interface{ detail::filter }; +static constexpr std::string_view interface { + detail::filter +}; /// \brief make dbus `member` match rule /// \tparam member_in filter value diff --git a/libs/dbus_util/inc/public/tfc/dbus/sml_interface.hpp b/libs/dbus_util/inc/public/tfc/dbus/sml_interface.hpp index 6d2dbdeb42..bdcc57e3fb 100644 --- a/libs/dbus_util/inc/public/tfc/dbus/sml_interface.hpp +++ b/libs/dbus_util/inc/public/tfc/dbus/sml_interface.hpp @@ -18,7 +18,9 @@ namespace tfc::dbus::sml { namespace tags { static constexpr std::string_view state_machine{ "StateMachine" }; -static constexpr std::string_view interface{ const_dbus_name }; +static constexpr std::string_view interface { + const_dbus_name +}; } // namespace tags namespace detail { diff --git a/libs/dbus_util/tests/examples/example_sml_interface.cpp b/libs/dbus_util/tests/examples/example_sml_interface.cpp index cb77745c04..90c420c183 100644 --- a/libs/dbus_util/tests/examples/example_sml_interface.cpp +++ b/libs/dbus_util/tests/examples/example_sml_interface.cpp @@ -34,7 +34,9 @@ auto main(int argc, char** argv) -> int { // Raw dbus connection, ipc_client also has a dbus connection which can be used through ipc_client.connection() auto const dbus{ std::make_shared(ctx) }; - tfc::dbus::sml::interface sml_interface{ dbus, "unique_key" }; + tfc::dbus::sml::interface sml_interface { + dbus, "unique_key" + }; using state_machine_t = boost::sml::sm >; diff --git a/libs/operation_mode/testing/example/liboperation_mode_example.cpp b/libs/operation_mode/testing/example/liboperation_mode_example.cpp index b3b5b114a3..231437fe21 100644 --- a/libs/operation_mode/testing/example/liboperation_mode_example.cpp +++ b/libs/operation_mode/testing/example/liboperation_mode_example.cpp @@ -12,7 +12,9 @@ auto main(int argc, char** argv) -> int { asio::io_context ctx{}; - tfc::operation::interface mode{ ctx }; + tfc::operation::interface mode { + ctx + }; mode.on_leave(tfc::operation::mode_e::stopped, [](tfc::operation::mode_e new_mode, tfc::operation::mode_e old_mode) { fmt::print("Leaving {} and going to: {}", enum_name(old_mode), enum_name(new_mode));