Skip to content

Commit

Permalink
More testing and finalizing of themis (#594)
Browse files Browse the repository at this point in the history
* Added empty test

* Some extra tests added and switched last_insert_id to a returning clause

Switched because the last_insert_id does not get updated on the conflict clause of the query

* Switched active_e to states_e
  • Loading branch information
Ómar Högni Guðmarsson authored Jun 27, 2024
1 parent bde60c1 commit 22b8ded
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 65 deletions.
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

0 comments on commit 22b8ded

Please sign in to comment.