Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More testing and finalizing of themis #594

Merged
merged 10 commits into from
Jun 27, 2024
6 changes: 3 additions & 3 deletions docs/design/snitch.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ The alarm system is exposed through the `com.skaginn3x.Alarm` interface.
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<struct { string description; string details; bool latching; enum alarm_level; std::map<locale, struct translations{ string description; string details}> ; }>
# 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; };
# Note for state = -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: state, x: startunixTimestamp, x: endUnixTimestamp) -> s -> json of: struct { string description; string details; bool latching; enum alarm_level; state_e state; std::uint64_t millisec_from_epoch; };
SetAlarm(i: alarm_id, as: variables)
ResetAlarm(i: alarm_id)
TryReset(i: alarm_id) # Transmits a signal to the alarm to reset itself
TryResetAll() # Transmits a signal to all alarms to reset themselfs
### Signals
AlarmActivationChanged(i: alarm_id, b: current_activation)
AlarmChanged(i: alarm_id, n: state)
TryReset(i: alarm_id)
TryResetAll()
### Properties
Expand Down
43 changes: 22 additions & 21 deletions exes/themis/inc/alarm_database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,20 +114,14 @@ CREATE TABLE IF NOT EXISTS AlarmVariables(
db_ << "BEGIN;";
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={};",
"CONFLICT (tfc_id, sha1sum) DO UPDATE SET registered_at={} RETURNING alarm_id;",
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) {
throw dbus_error("Failed to insert alarm into database");
}
alarm_id = static_cast<snitch::api::alarm_id_t>(insert_id);
ms_count_registered_at) >>
[&](snitch::api::alarm_id_t id) { alarm_id = 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);
}
[[maybe_unused]] bool was_reset = reset_alarm(alarm_id);
db_ << "COMMIT;";
} catch (std::exception& e) {
// Rollback the transaction and rethrow
Expand Down Expand Up @@ -244,7 +238,7 @@ 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), std::to_underlying(tfc::snitch::api::active_e::active));
alarm_id, milliseconds_since_epoch(tp), std::to_underlying(tfc::snitch::api::state_e::active));
activation_id = static_cast<tfc::snitch::api::activation_id_t>(db_.last_insert_rowid());

for (auto& [key, value] : variables) {
Expand All @@ -258,15 +252,22 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum;
db_ << "COMMIT;";
return activation_id;
}
auto reset_alarm(snitch::api::alarm_id_t activation_id, std::optional<tfc::snitch::api::time_point> tp = {}) -> void {
/**
* @brief Reset an alarm in the database
* @param activation_id the activation_id of the alarm to reset
* @param tp an optional timepoint
* @return true if the alarm was reset
*/
[[nodiscard]] auto reset_alarm(snitch::api::alarm_id_t activation_id, std::optional<tfc::snitch::api::time_point> tp = {})
-> bool {
if (!is_activation_high(activation_id)) {
throw dbus_error("Cannot reset an inactive activation");
return false;
}
db_ << fmt::format("UPDATE AlarmActivations SET activation_level = {}, reset_time = {} WHERE activation_id = {};",
std::to_underlying(tfc::snitch::api::active_e::inactive), milliseconds_since_epoch(tp),
activation_id);
std::to_underlying(tfc::snitch::api::state_e::inactive), milliseconds_since_epoch(tp), activation_id);
return true;
}
auto set_activation_status(snitch::api::alarm_id_t activation_id, tfc::snitch::api::active_e activation) -> void {
auto set_activation_status(snitch::api::alarm_id_t activation_id, tfc::snitch::api::state_e activation) -> void {
if (!is_activation_high(activation_id)) {
throw dbus_error("Cannot reset an inactive activation");
}
Expand All @@ -277,7 +278,7 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum;
auto get_activation_id_for_active_alarm(snitch::api::alarm_id_t alarm_id) -> std::optional<snitch::api::activation_id_t> {
std::optional<snitch::api::activation_id_t> 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::to_underlying(tfc::snitch::api::state_e::active)) >>
[&](std::uint64_t id) { activation_id = id; };
return activation_id;
}
Expand All @@ -286,7 +287,7 @@ ON Alarms.sha1sum = AlarmTranslations.sha1sum;
std::uint64_t start_count,
std::uint64_t count,
tfc::snitch::level_e level,
tfc::snitch::api::active_e active,
tfc::snitch::api::state_e active,
std::optional<tfc::snitch::api::time_point> start,
std::optional<tfc::snitch::api::time_point> end)
-> std::vector<tfc::snitch::api::activation> {
Expand Down Expand Up @@ -314,7 +315,7 @@ WHERE activation_time >= {} AND activation_time <= {})",
if (level != tfc::snitch::level_e::all) {
populated_query += fmt::format(" AND alarm_level = {}", std::to_underlying(level));
}
if (active != tfc::snitch::api::active_e::all) {
if (active != tfc::snitch::api::state_e::all) {
populated_query += fmt::format(" AND activation_level = {}", std::to_underlying(active));
}
populated_query += fmt::format(" LIMIT {} OFFSET {};", count, start_count);
Expand All @@ -323,7 +324,7 @@ WHERE activation_time >= {} AND activation_time <= {})",

db_ << populated_query >> [&](snitch::api::activation_id_t activation_id, snitch::api::alarm_id_t alarm_id,
std::int64_t activation_time, std::optional<std::int64_t> reset_time,
std::underlying_type_t<snitch::api::active_e> activation_level,
std::underlying_type_t<snitch::api::state_e> activation_level,
std::optional<std::string> primary_details, std::optional<std::string> primary_description,
std::optional<std::string> backup_details, std::optional<std::string> backup_description,
bool alarm_latching, std::underlying_type_t<snitch::level_e> alarm_level) {
Expand All @@ -338,7 +339,7 @@ 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<snitch::api::active_e>(activation_level),
static_cast<snitch::api::state_e>(activation_level),
static_cast<snitch::level_e>(alarm_level), alarm_latching,
timepoint_from_milliseconds(activation_time), final_reset_time, in_locale);
};
Expand Down
44 changes: 33 additions & 11 deletions exes/themis/inc/dbus_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@ using namespace tfc::snitch::api::dbus;
using std::string_view_literals::operator""sv;
using dbus_error = tfc::dbus::exception::runtime;

using tfc::snitch::level_e;
using tfc::snitch::api::alarm_id_t;
using tfc::snitch::api::state_e;

class interface {
public:
explicit interface(std::shared_ptr<sdbusplus::asio::connection> connection, tfc::themis::alarm_database& database)
: database_(database) {
connection_ = connection;
: connection_(std::move(connection)), database_(database) {
// Initialize the object server and request the service name
object_server_ = std::make_unique<sdbusplus::asio::object_server>(connection_);
connection_->request_name(service_name.data());
interface_ = object_server_->add_unique_interface(object_path.data(), interface_name.data());
Expand All @@ -48,16 +52,26 @@ class interface {
auto alarm_id = database.register_alarm_en(tfc_id, description, details, latching,
static_cast<tfc::snitch::level_e>(alarm_level));
dbus_ids_to_monitor_[sender].push_back(alarm_id);
notify_alarm_state(alarm_id, state_e::inactive);
return alarm_id;
});

interface_->register_method(
std::string(methods::set_alarm),
[&](snitch::api::alarm_id_t alarm_id, const std::unordered_map<std::string, std::string>& args) -> std::uint64_t {
return database.set_alarm(alarm_id, args);
auto activation_id = database.set_alarm(alarm_id, args);
notify_alarm_state(alarm_id, state_e::active);
return activation_id;
});
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::reset_alarm), [&](snitch::api::alarm_id_t alarm_id) -> void {
if (!database.reset_alarm(alarm_id)) {
throw dbus_error("Cannot reset an inactive activation");
} else {
// Notify the change in alarm status of this alarm
notify_alarm_state(alarm_id, state_e::inactive);
}
});

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());
Expand All @@ -67,6 +81,7 @@ class interface {
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());
Expand All @@ -75,25 +90,25 @@ class interface {
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<level_e> alarm_level, std::underlying_type_t<active_e> active,
std::underlying_type_t<level_e> alarm_level, std::underlying_type_t<state_e> 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);
auto const activations_str{ glz::write_json(database.list_activations(
locale, start_count, count, static_cast<tfc::snitch::level_e>(alarm_level),
static_cast<tfc::snitch::api::active_e>(active), cstart, cend)) };
static_cast<tfc::snitch::api::state_e>(active), cstart, cend)) };
if (!activations_str) {
throw dbus_error("Failed to serialize activations");
}
return activations_str.value();
});

// Signal alarm_id, current_activation, ack_status
interface_->register_signal<std::tuple<std::uint64_t, bool, bool>>(std::string(signals::alarm_activation_changed));
interface_->register_signal<std::tuple<tfc::snitch::api::alarm_id_t, std::underlying_type_t<tfc::snitch::api::state_e>>>(
std::string(signals::alarm_activation_changed));
interface_->register_signal<std::uint64_t>(std::string(signals::try_reset));
interface_->register_signal<void>(std::string(signals::try_reset_all));
name_lost_match_ = std::make_unique<sdbusplus::bus::match::match>(*connection_, match_rule_.data(),
Expand All @@ -108,6 +123,12 @@ class interface {
static constexpr std::string_view name_owner_changed_ = "NameOwnerChanged"sv;
static constexpr std::string_view match_rule_ = tfc::dbus::match::rules::
make_match_rule<dbus_name_, dbus_interface_, dbus_path_, name_owner_changed_, tfc::dbus::match::rules::type::signal>();

auto notify_alarm_state(alarm_id_t alarm_id, state_e state) -> void {
sdbusplus::message_t alarm_change_message = interface_->new_signal(signals::alarm_activation_changed.data());
alarm_change_message.append(std::tuple(alarm_id, std::to_underlying(state)));
alarm_change_message.signal_send();
}
auto match_callback(sdbusplus::message_t& msg) -> void {
std::tuple<std::string, std::string, std::string> container;
sdbusplus::utility::read_into_tuple(container, msg);
Expand All @@ -121,7 +142,8 @@ class interface {
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);
database_.set_activation_status(activation.value(), tfc::snitch::api::state_e::unknown);
notify_alarm_state(alarm_id, state_e::unknown);
}
}
}
Expand Down
Loading
Loading