Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
#
|
||||
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
# template version 3
|
||||
#
|
||||
|
||||
# module setup:
|
||||
# - ${MODULE_NAME}: module name
|
||||
ev_setup_cpp_module()
|
||||
|
||||
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
|
||||
# insert your custom targets and additional config variables here
|
||||
target_link_libraries(${MODULE_NAME}
|
||||
PRIVATE
|
||||
Pal::Sigslot
|
||||
everest::run_application
|
||||
everest::system
|
||||
)
|
||||
|
||||
target_sources(${MODULE_NAME}
|
||||
PRIVATE
|
||||
"diagnostics_handler.cpp"
|
||||
"rauc_dbus.cpp"
|
||||
)
|
||||
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
|
||||
|
||||
target_sources(${MODULE_NAME}
|
||||
PRIVATE
|
||||
"main/systemImpl.cpp"
|
||||
)
|
||||
|
||||
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
|
||||
# insert other things like install cmds etc here
|
||||
install(
|
||||
PROGRAMS
|
||||
constants.env
|
||||
diagnostics_uploader.sh
|
||||
diagnostics_collector.sh
|
||||
DESTINATION "${EVEREST_MODULE_INSTALL_PREFIX}/${MODULE_NAME}"
|
||||
)
|
||||
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
|
||||
@@ -0,0 +1,126 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "Linux_Systemd_Rauc.hpp"
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
#include <everest/system/safe_system.hpp>
|
||||
|
||||
namespace {
|
||||
template <typename T> void safe_extract(T& dst, const Object& src, const std::string_view& item) {
|
||||
try {
|
||||
std::string s{item};
|
||||
dst = src.at(s);
|
||||
} catch (const std::exception& ex) {
|
||||
EVLOG_error << "Store[" << item << "] error: " << ex.what();
|
||||
if constexpr (std::is_integral_v<T>) {
|
||||
dst = module::Rauc::request_id_default;
|
||||
} else {
|
||||
dst.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace module {
|
||||
|
||||
void Linux_Systemd_Rauc::init() {
|
||||
invoke_init(*p_main);
|
||||
rauc.configure(this->config.VerifyUpdateScriptPath);
|
||||
|
||||
store_path = info.id + "_update_transaction";
|
||||
|
||||
// This is a transaction we should store permanently
|
||||
rauc.signal_store_update_transaction.connect([this](Rauc::UpdateTransaction t) {
|
||||
EVLOG_info << "Store update transaction: " << t.boot_slot << ' ' << t.primary_slot << " id: " << t.request_id;
|
||||
Object tx = {
|
||||
{"current_primary_slot", t.primary_slot}, {"current_boot_slot", t.boot_slot}, {"request_id", t.request_id}};
|
||||
r_store->call_store(store_path, tx);
|
||||
});
|
||||
|
||||
rauc.signal_remove_update_transaction.connect([this]() {
|
||||
EVLOG_info << "Update transaction removed.";
|
||||
r_store->call_delete(store_path);
|
||||
});
|
||||
|
||||
rauc.signal_firmware_update_status.connect(
|
||||
[this](const types::system::FirmwareUpdateStatusEnum& status, int32_t request_id) {
|
||||
EVLOG_info << "Report status to OCPP: " << types::system::firmware_update_status_enum_to_string(status)
|
||||
<< " Request id: " << request_id;
|
||||
p_main->publish_firmware_update_status({status, request_id});
|
||||
|
||||
if (status == types::system::FirmwareUpdateStatusEnum::InstallRebooting) {
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(this->firmware_update_progress_mx);
|
||||
if (this->firmware_update_waiting_for_ocpp_unblocking) {
|
||||
EVLOG_info << "Reboot is blocked by OCPP (waiting for 'allow_firmware_installation' call)";
|
||||
this->firmware_update_reboot_scheduled = true;
|
||||
} else {
|
||||
reboot_after_firmware_update();
|
||||
}
|
||||
}
|
||||
|
||||
if (status == types::system::FirmwareUpdateStatusEnum::InstallVerificationFailed) {
|
||||
EVLOG_info << "Resetting firmware update state due to reported 'InstallVerificationFailed' status.";
|
||||
std::unique_lock<std::recursive_mutex> lock(this->firmware_update_progress_mx);
|
||||
this->firmware_update_waiting_for_ocpp_unblocking = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Linux_Systemd_Rauc::reboot_after_firmware_update() {
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(this->firmware_update_progress_mx);
|
||||
this->firmware_update_reboot_scheduled = false;
|
||||
}
|
||||
EVLOG_error << "-------------- Reboot after installation of update in 10 seconds ---------------";
|
||||
sleep(10);
|
||||
try {
|
||||
auto [cmd, args] = everest::lib::system::split_command_line(config.RebootCommand);
|
||||
auto res = everest::lib::system::safe_system(cmd, &args);
|
||||
if (res.status != everest::lib::system::CommandExecutionStatus::CMD_SUCCESS || res.code != 0) {
|
||||
EVLOG_error << "Unable to trigger reboot: ("
|
||||
<< everest::lib::system::cmd_execution_status_to_string(res.status) << ": "
|
||||
<< std::to_string(res.code) << ")";
|
||||
EVLOG_info << "Failed command: '" << everest::lib::system::command_string_repr(cmd, args) << "'";
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
EVLOG_error << "Configured reboot command is invalid: " << ex.what();
|
||||
}
|
||||
}
|
||||
|
||||
void Linux_Systemd_Rauc::ready() {
|
||||
invoke_ready(*p_main);
|
||||
|
||||
// Check if we are booting directly after an update install,
|
||||
// in this case close the update process on the CSMS
|
||||
if (r_store->call_exists(store_path)) {
|
||||
Rauc::UpdateTransaction tx;
|
||||
auto t = std::get<Object>(r_store->call_load(store_path));
|
||||
safe_extract(tx.boot_slot, t, "current_boot_slot");
|
||||
safe_extract(tx.primary_slot, t, "current_primary_slot");
|
||||
safe_extract(tx.request_id, t, "request_id");
|
||||
rauc.check_previous_transaction(tx);
|
||||
}
|
||||
}
|
||||
|
||||
void Linux_Systemd_Rauc::firmware_update_may_proceed_with_reboot_callback() {
|
||||
std::lock_guard<std::recursive_mutex> lock(this->firmware_update_progress_mx);
|
||||
this->firmware_update_waiting_for_ocpp_unblocking = false;
|
||||
if (this->firmware_update_reboot_scheduled) {
|
||||
this->reboot_after_firmware_update();
|
||||
}
|
||||
}
|
||||
|
||||
void Linux_Systemd_Rauc::install_firmware_bundle(const std::string& filename, int32_t request_id) {
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(this->firmware_update_progress_mx);
|
||||
this->firmware_update_waiting_for_ocpp_unblocking = true;
|
||||
}
|
||||
this->rauc.install_bundle(filename, request_id);
|
||||
}
|
||||
|
||||
} // namespace module
|
||||
@@ -0,0 +1,83 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef LINUX_SYSTEMD_RAUC_HPP
|
||||
#define LINUX_SYSTEMD_RAUC_HPP
|
||||
|
||||
//
|
||||
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
// template version 2
|
||||
//
|
||||
|
||||
#include "ld-ev.hpp"
|
||||
|
||||
// headers for provided interface implementations
|
||||
#include <generated/interfaces/system/Implementation.hpp>
|
||||
|
||||
// headers for required interface implementations
|
||||
#include <generated/interfaces/kvs/Interface.hpp>
|
||||
|
||||
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
|
||||
// insert your custom include headers here
|
||||
#include "rauc_dbus.hpp"
|
||||
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
|
||||
|
||||
namespace module {
|
||||
|
||||
struct Conf {
|
||||
double DefaultRetries;
|
||||
double DefaultRetryInterval;
|
||||
std::string OCPPLogPath;
|
||||
std::string SessionLogPath;
|
||||
std::string RebootCommand;
|
||||
std::string VerifyUpdateScriptPath;
|
||||
};
|
||||
|
||||
class Linux_Systemd_Rauc : public Everest::ModuleBase {
|
||||
public:
|
||||
Linux_Systemd_Rauc() = delete;
|
||||
Linux_Systemd_Rauc(const ModuleInfo& info, std::unique_ptr<systemImplBase> p_main, std::unique_ptr<kvsIntf> r_store,
|
||||
Conf& config) :
|
||||
ModuleBase(info), p_main(std::move(p_main)), r_store(std::move(r_store)), config(config){};
|
||||
|
||||
const std::unique_ptr<systemImplBase> p_main;
|
||||
const std::unique_ptr<kvsIntf> r_store;
|
||||
const Conf& config;
|
||||
|
||||
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
|
||||
// insert your public definitions here
|
||||
Rauc rauc;
|
||||
void firmware_update_may_proceed_with_reboot_callback();
|
||||
void install_firmware_bundle(const std::string& filename, int32_t request_id);
|
||||
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
|
||||
|
||||
protected:
|
||||
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
|
||||
// insert your protected definitions here
|
||||
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
|
||||
|
||||
private:
|
||||
friend class LdEverest;
|
||||
void init();
|
||||
void ready();
|
||||
|
||||
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
|
||||
// insert your private definitions here
|
||||
std::string store_path;
|
||||
|
||||
std::recursive_mutex firmware_update_progress_mx;
|
||||
bool firmware_update_waiting_for_ocpp_unblocking =
|
||||
false; // Set to true in case of OTA update via OCPP; set to false when OCPP has signaled that installation may
|
||||
// proceed
|
||||
bool firmware_update_reboot_scheduled = false; // Set to true once a firmware update is installed but a restart has
|
||||
// been blocked by firmware_update_waiting_for_ocpp_unblocking
|
||||
void reboot_after_firmware_update();
|
||||
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
|
||||
};
|
||||
|
||||
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
|
||||
// insert other definitions here
|
||||
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
|
||||
|
||||
} // namespace module
|
||||
|
||||
#endif // LINUX_SYSTEMD_RAUC_HPP
|
||||
@@ -0,0 +1,27 @@
|
||||
CONNECTION_TIMEOUT=20
|
||||
|
||||
DOWNLOADED="Downloaded"
|
||||
DOWNLOADING="Downloading"
|
||||
DOWNLOAD_FAILED="DownloadFailed"
|
||||
DOWNLOAD_SCHEDULED="DownloadScheduled"
|
||||
DOWNLOAD_PAUSED="DownloadPaused"
|
||||
|
||||
IDLE="Idle"
|
||||
|
||||
INSTALLATION_FAILED="InstallationFailed"
|
||||
INSTALLING="Installing"
|
||||
INSTALLED="Installed"
|
||||
INSTALL_REBOOTING="InstallRebooting"
|
||||
INSTALL_SCHEDULED="InstallScheduled"
|
||||
INSTALL_VERIFICATION_FAILED="InstallVerificationFailed"
|
||||
|
||||
PERMISSION_DENIED="PermissionDenied"
|
||||
NOT_SUPPORTED_OPERATION="NotSupportedOperation"
|
||||
BAD_MESSAGE="BadMessage"
|
||||
|
||||
INVALID_SIGNATURE="InvalidSignature"
|
||||
SIGNATURE_VERIFIED="SignatureVerified"
|
||||
|
||||
UPLOADED="Uploaded"
|
||||
UPLOAD_FAILURE="UploadFailure"
|
||||
UPLOADING="Uploading"
|
||||
186
tools/EVerest-main/modules/Misc/Linux_Systemd_Rauc/diagnostics_collector.sh
Executable file
186
tools/EVerest-main/modules/Misc/Linux_Systemd_Rauc/diagnostics_collector.sh
Executable file
@@ -0,0 +1,186 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright Pionix GmbH and Contributors to EVerest
|
||||
#
|
||||
#
|
||||
# time windowed collection is supported as follows:
|
||||
# - systemd journal supports "since" and "until" to specify the time window
|
||||
# - OCPP logs are rotated daily and a log file is included when its modification
|
||||
# time falls between "since" and "until"
|
||||
# - Completed session logs are included when their modification time falls
|
||||
# between "since" and "until" (i.e. the end of session time is used)
|
||||
# - In-progress session logs are included when their modification time falls
|
||||
# between "since" and now i.e. where "until" is specified then in-progress
|
||||
# sessions are unlikely to be included
|
||||
|
||||
error() {
|
||||
echo "$script error: $1" 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "Usage: $script <journal|ocpp|session>"
|
||||
echo " [--dir /full/path/to/log/directory]"
|
||||
echo " [--since \"yyyy-mm-dd hh:mm:ss\"]"
|
||||
echo " [--until \"yyyy-mm-dd hh:mm:ss\"]"
|
||||
exit 2
|
||||
}
|
||||
|
||||
section_start() {
|
||||
printf "### START $1\n"
|
||||
}
|
||||
|
||||
section_end() {
|
||||
printf "\n### END $1\n"
|
||||
}
|
||||
|
||||
# remove T from date/time strings
|
||||
# 2023-12-18T16:06:03.435Z -> 2023-12-18 16:06:03.435Z
|
||||
reformat_date() {
|
||||
pre=$(echo $1 | cut -c1-10)
|
||||
post=$(echo $1 | cut -c12-19)
|
||||
if [ -n "$pre" ] && [ -n "$post" ]; then
|
||||
echo "$pre $post"
|
||||
fi
|
||||
}
|
||||
|
||||
# obtain the modification time of a file as a timestamp
|
||||
modify_ts() {
|
||||
if [ -f "$1" ]; then
|
||||
stat -c %Y "$1"
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
}
|
||||
|
||||
# convert the supplied date/time to a timestamp
|
||||
date_ts() {
|
||||
if [ -n "$1" ]; then
|
||||
date -u +%s -d "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
# output file if modified between optional dates
|
||||
# $1 file name
|
||||
# $2 optional not before date
|
||||
# $3 optional not after date
|
||||
# $4 line to output before file
|
||||
output_file() {
|
||||
if [ -f "$1" ]; then
|
||||
local modify_time=$(modify_ts "$1")
|
||||
local not_before=$(date_ts "$2")
|
||||
local not_after=$(date_ts "$3")
|
||||
local output=1
|
||||
[ -n "$not_before" ] && [ $modify_time -lt $not_before ] && output=0
|
||||
[ -n "$not_after" ] && [ $modify_time -gt $not_after ] && output=0
|
||||
if [ $output -ne 0 ]; then
|
||||
[ -n "$4" ] && echo "$4"
|
||||
sed -e '/^$/d' "$1"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# output systemd journal
|
||||
# $1 optional not before date
|
||||
# $2 optional not after date
|
||||
#
|
||||
# journalctl doesn't like empty arguments
|
||||
do_journal() {
|
||||
section_start JOURNAL
|
||||
if [ -n "$1" ]; then
|
||||
if [ -n "$2" ]; then
|
||||
journalctl --output=json --no-pager "--since=$1" "--until=$2"
|
||||
else
|
||||
journalctl --output=json --no-pager "--since=$1"
|
||||
fi
|
||||
else
|
||||
journalctl --output=json --no-pager
|
||||
fi
|
||||
local res=$?
|
||||
section_end JOURNAL
|
||||
return $res
|
||||
}
|
||||
|
||||
# output Everest OCPP logs
|
||||
# $1 Everest OCPP log directory
|
||||
# $2 optional not before date
|
||||
# $3 optional not after date
|
||||
do_ocpp() {
|
||||
[ -z "$1" ] && error "Missing OCPP log directory"
|
||||
local res=0
|
||||
section_start OCPP
|
||||
if [ -d "$1" ]; then
|
||||
find "$1" -name \*.log\* | sort -r | while read ocpp
|
||||
do
|
||||
output_file "$ocpp" "$2" "$3"
|
||||
done
|
||||
res=$?
|
||||
fi
|
||||
section_end OCPP
|
||||
return $res
|
||||
}
|
||||
|
||||
# output Everest charging session logs
|
||||
# $1 Everest session log directory
|
||||
# $2 optional not before date
|
||||
# $3 optional not after date
|
||||
do_session() {
|
||||
[ -z "$1" ] && error "Missing session log directory"
|
||||
local name
|
||||
local res=0
|
||||
section_start SESSION
|
||||
if [ -d "$1" ]; then
|
||||
find "$1" -type d | sort -n | while read session
|
||||
do
|
||||
name=$(basename "${session}")
|
||||
if [ -f "${session}/eventlog.csv" ]; then
|
||||
output_file "${session}/eventlog.csv" "$2" "$3" "--- ${name}"
|
||||
res=$?
|
||||
fi
|
||||
if [ -f "${session}/incomplete-eventlog.csv" ]; then
|
||||
output_file "${session}/incomplete-eventlog.csv" "$2" "$3" "--- ${name}"
|
||||
res=$?
|
||||
fi
|
||||
done
|
||||
fi
|
||||
section_end SESSION
|
||||
return $res
|
||||
}
|
||||
|
||||
script=$(basename $0)
|
||||
[ -z "$1" ] && error "Missing sub-command"
|
||||
cmd=$(basename "${1}")
|
||||
shift
|
||||
|
||||
TEMP=$(getopt -o "" --long 'dir:,since:,until:' -n "$script" -- "$@")
|
||||
if [ $? -ne 0 ]; then
|
||||
error "getopt parsing"
|
||||
fi
|
||||
|
||||
# Note the quotes around "$TEMP": they are essential!
|
||||
eval set -- "$TEMP"
|
||||
unset TEMP
|
||||
|
||||
dir=
|
||||
since=
|
||||
until=
|
||||
|
||||
while true
|
||||
do
|
||||
case "$1" in
|
||||
"--dir"|"--since"|"--until") eval "${1#--}=\"$2\""; shift 2; continue;;
|
||||
"--") shift; break;;
|
||||
esac
|
||||
done
|
||||
|
||||
since=$(reformat_date "$since")
|
||||
until=$(reformat_date "$until")
|
||||
|
||||
case $cmd in
|
||||
journal) do_journal "$since" "$until";;
|
||||
ocpp) do_ocpp "$dir" "$since" "$until";;
|
||||
session) do_session "$dir" "$since" "$until";;
|
||||
*) usage;;
|
||||
esac
|
||||
@@ -0,0 +1,79 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "diagnostics_handler.hpp"
|
||||
#include <everest/logging.hpp>
|
||||
#include <everest/system/safe_system.hpp>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace module {
|
||||
|
||||
DiagnosticsHandler::log_result_t DiagnosticsHandler::create_log(const std::string& filename,
|
||||
const std::optional<std::string>& from,
|
||||
const std::optional<std::string>& to) {
|
||||
log_result_t result = log_result_t::success;
|
||||
|
||||
int fd;
|
||||
if ((fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR | S_IXUSR)) == -1) {
|
||||
EVLOG_error << "Unable to create file journal file: " << filename << " (" << errno << ")";
|
||||
result = log_result_t::error_file;
|
||||
} else {
|
||||
result = create_journal_log(fd, from, to);
|
||||
if (result == log_result_t::success) {
|
||||
result = create_ocpp_log(fd, from, to);
|
||||
}
|
||||
if (result == log_result_t::success) {
|
||||
result = create_session_log(fd, from, to);
|
||||
}
|
||||
(void)close(fd);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DiagnosticsHandler::log_result_t DiagnosticsHandler::collect_logs(int fd, const char* logType,
|
||||
const std::optional<std::string>& logDir,
|
||||
const std::optional<std::string>& from,
|
||||
const std::optional<std::string>& to) {
|
||||
log_result_t result = log_result_t::success;
|
||||
|
||||
std::string arg_cmd = "diagnostics_collector.sh";
|
||||
|
||||
std::vector<std::string> args;
|
||||
std::string cmd = script_dir + "/" + arg_cmd;
|
||||
|
||||
args.push_back(arg_cmd);
|
||||
args.emplace_back(logType);
|
||||
|
||||
if (logDir.has_value()) {
|
||||
args.emplace_back("--dir");
|
||||
args.push_back(logDir.value());
|
||||
}
|
||||
if (from.has_value()) {
|
||||
args.emplace_back("--since");
|
||||
args.push_back(from.value());
|
||||
}
|
||||
if (to.has_value()) {
|
||||
args.emplace_back("--until");
|
||||
args.push_back(to.value());
|
||||
}
|
||||
|
||||
const auto res = everest::lib::system::safe_system(fd, cmd, &args);
|
||||
if (res.status != everest::lib::system::CommandExecutionStatus::CMD_SUCCESS || res.code != 0) {
|
||||
EVLOG_error << "Unable to extract journal logs from:" << from.value_or("<not specified>")
|
||||
<< " to:" << to.value_or("<not specified>") << " ("
|
||||
<< everest::lib::system::cmd_execution_status_to_string(res.status) << ": "
|
||||
<< std::to_string(res.code) << ")";
|
||||
EVLOG_info << "Failed command: '" << everest::lib::system::command_string_repr(cmd, args) << "'";
|
||||
result = log_result_t::error_parameter;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace module
|
||||
@@ -0,0 +1,58 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef DIAGNOSTICS_HANDLER_HPP
|
||||
#define DIAGNOSTICS_HANDLER_HPP
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace module {
|
||||
|
||||
class DiagnosticsHandler {
|
||||
protected:
|
||||
std::string script_dir;
|
||||
std::string ocpp_dir;
|
||||
std::string session_dir;
|
||||
|
||||
public:
|
||||
enum class log_result_t {
|
||||
success,
|
||||
error,
|
||||
error_file,
|
||||
error_parameter,
|
||||
};
|
||||
DiagnosticsHandler() = delete;
|
||||
DiagnosticsHandler(const std::string& scriptDir, const std::string& ocppDir, const std::string& sessionDir) :
|
||||
script_dir(scriptDir), ocpp_dir(ocppDir), session_dir(sessionDir) {
|
||||
}
|
||||
log_result_t create_log(const std::string& filename, const std::optional<std::string>& from,
|
||||
const std::optional<std::string>& to);
|
||||
|
||||
protected:
|
||||
log_result_t create_journal_log(int fd, const std::optional<std::string>& from,
|
||||
const std::optional<std::string>& to) {
|
||||
return collect_logs(fd, "journal", std::nullopt, from, to);
|
||||
}
|
||||
log_result_t create_ocpp_log(int fd, const std::optional<std::string>& from, const std::optional<std::string>& to) {
|
||||
return collect_logs(fd, "ocpp", ocpp_dir, from, to);
|
||||
}
|
||||
log_result_t create_session_log(int fd, const std::optional<std::string>& from,
|
||||
const std::optional<std::string>& to) {
|
||||
return collect_logs(fd, "session", session_dir, from, to);
|
||||
}
|
||||
|
||||
/// @brief collect logs between two optional dates
|
||||
/// @param fd - the file descriptor to write the logs to
|
||||
/// @param logType - journal/ocpp/session
|
||||
/// @param logDir - directory location for OCPP and session logs
|
||||
/// @param from - format: "yyyy-mm-dd hh:mm"
|
||||
/// @param to - format: "yyyy-mm-dd hh:mm"
|
||||
/// @return result
|
||||
log_result_t collect_logs(int fd, const char* logType, const std::optional<std::string>& logDir,
|
||||
const std::optional<std::string>& from, const std::optional<std::string>& to);
|
||||
};
|
||||
|
||||
} // namespace module
|
||||
|
||||
#endif // DIAGNOSTICS_HANDLER_HPP
|
||||
27
tools/EVerest-main/modules/Misc/Linux_Systemd_Rauc/diagnostics_uploader.sh
Executable file
27
tools/EVerest-main/modules/Misc/Linux_Systemd_Rauc/diagnostics_uploader.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright Pionix GmbH and Contributors to EVerest
|
||||
#
|
||||
|
||||
|
||||
. "${1}"
|
||||
|
||||
echo "$UPLOADING"
|
||||
sleep 2
|
||||
curl -L --progress-bar --connect-timeout "$CONNECTION_TIMEOUT" -T "${4}" "${2}"
|
||||
curl_exit_code=$?
|
||||
if [[ $curl_exit_code -eq 0 ]]; then
|
||||
echo "$UPLOADED"
|
||||
elif [[ $curl_exit_code -eq 67 ]] || [[ $curl_exit_code -eq 35 ]] || [[ $curl_exit_code -eq 69 ]] ||
|
||||
[[ $curl_exit_code -eq 9 ]]; then
|
||||
echo "$PERMISSION_DENIED"
|
||||
elif [[ $curl_exit_code -eq 3 ]] || [[ $curl_exit_code -eq 6 ]] || [[ $curl_exit_code -eq 10 ]] ||
|
||||
[[ $curl_exit_code -eq 87 ]]; then
|
||||
echo "$BAD_MESSAGE"
|
||||
elif [[ $curl_exit_code -eq 1 ]]; then
|
||||
echo "$NOT_SUPPORTED_OPERATION"
|
||||
else
|
||||
echo "$UPLOAD_FAILURE"
|
||||
fi
|
||||
@@ -0,0 +1,191 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "systemImpl.hpp"
|
||||
#include "diagnostics_handler.hpp"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <everest/run_application/run_application.hpp>
|
||||
|
||||
using namespace everest::run_application;
|
||||
|
||||
namespace module {
|
||||
namespace main {
|
||||
|
||||
const std::string CONSTANTS = "constants.env";
|
||||
const std::string DIAGNOSTICS_UPLOADER = "diagnostics_uploader.sh";
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// FIXME (aw): this function needs to be refactored into some kind of utility library
|
||||
fs::path create_temp_file(const fs::path& dir, const std::string& prefix) {
|
||||
const std::string fn_template = (dir / prefix).string() + "XXXXXX" + std::string(1, '\0');
|
||||
std::vector<char> fn_template_buffer{fn_template.begin(), fn_template.end()};
|
||||
|
||||
// mkstemp needs to have at least 6 XXXXXX at the end and it will replace these
|
||||
// with a valid file name
|
||||
auto fd = mkstemp(fn_template_buffer.data());
|
||||
|
||||
if (fd == -1) {
|
||||
EVLOG_AND_THROW(Everest::EverestBaseRuntimeError("Failed to create temporary file at: " + fn_template));
|
||||
}
|
||||
|
||||
// close the file descriptor
|
||||
close(fd);
|
||||
|
||||
return fn_template_buffer.data();
|
||||
}
|
||||
|
||||
void systemImpl::init() {
|
||||
this->scripts_path = mod->info.paths.libexec;
|
||||
}
|
||||
|
||||
void systemImpl::ready() {
|
||||
}
|
||||
|
||||
types::system::UpdateFirmwareResponse
|
||||
systemImpl::handle_update_firmware(types::system::FirmwareUpdateRequest& firmware_update_request) {
|
||||
// FIXME: implement planned updates at a specific time
|
||||
// FIXME: we don't care about the certificate and signature provided as an argument for now.
|
||||
// RAUC will not use them anyhow and updates will be equally secure whether they are launched by OCPP secure update
|
||||
// mechanism or the old non-secure mechanism.
|
||||
if (mod->rauc.is_idle()) {
|
||||
EVLOG_info << "Installing bundle from URL: " << firmware_update_request.location;
|
||||
this->mod->install_firmware_bundle(firmware_update_request.location, firmware_update_request.request_id);
|
||||
return types::system::UpdateFirmwareResponse::Accepted;
|
||||
} else {
|
||||
return types::system::UpdateFirmwareResponse::Rejected;
|
||||
}
|
||||
}
|
||||
|
||||
types::system::UploadLogsResponse
|
||||
systemImpl::handle_upload_logs(types::system::UploadLogsRequest& upload_logs_request) {
|
||||
types::system::UploadLogsResponse response;
|
||||
|
||||
if (this->log_upload_running) {
|
||||
response.upload_logs_status = types::system::UploadLogsStatus::AcceptedCanceled;
|
||||
} else {
|
||||
response.upload_logs_status = types::system::UploadLogsStatus::Accepted;
|
||||
}
|
||||
|
||||
const auto date_time = Everest::Date::to_rfc3339(date::utc_clock::now());
|
||||
const auto diagnostics_file_path = create_temp_file(fs::temp_directory_path(), "diagnostics-" + date_time);
|
||||
const auto diagnostics_file_name = diagnostics_file_path.filename().string();
|
||||
|
||||
response.upload_logs_status = types::system::UploadLogsStatus::Accepted;
|
||||
response.file_name = diagnostics_file_name;
|
||||
|
||||
// populate file with available logs within the specified time window
|
||||
DiagnosticsHandler diag(mod->info.paths.libexec, mod->config.OCPPLogPath, mod->config.SessionLogPath);
|
||||
const auto create_result = diag.create_log(diagnostics_file_path.c_str(), upload_logs_request.oldest_timestamp,
|
||||
upload_logs_request.latest_timestamp);
|
||||
|
||||
this->upload_logs_thread =
|
||||
std::thread([this, create_result, upload_logs_request, diagnostics_file_name, diagnostics_file_path]() {
|
||||
if (this->log_upload_running) {
|
||||
EVLOG_info << "Received Log upload request and log upload already running - cancelling current upload";
|
||||
this->interrupt_log_upload.exchange(true);
|
||||
EVLOG_info << "Waiting for other log upload to finish...";
|
||||
std::unique_lock<std::mutex> lk(this->log_upload_mutex);
|
||||
this->log_upload_cv.wait(lk, [this]() { return !this->log_upload_running; });
|
||||
EVLOG_info << "Previous Log upload finished!";
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lg(this->log_upload_mutex);
|
||||
EVLOG_info << "Starting upload of log file";
|
||||
this->interrupt_log_upload.exchange(false);
|
||||
this->log_upload_running = true;
|
||||
const auto diagnostics_uploader = this->scripts_path / DIAGNOSTICS_UPLOADER;
|
||||
const auto constants = this->scripts_path / CONSTANTS;
|
||||
|
||||
std::vector<std::string> args = {constants.string(), upload_logs_request.location, diagnostics_file_name,
|
||||
diagnostics_file_path.string()};
|
||||
bool uploaded = false;
|
||||
int32_t retries = 0;
|
||||
const auto total_retries = upload_logs_request.retries.value_or(this->mod->config.DefaultRetries);
|
||||
const auto retry_interval =
|
||||
upload_logs_request.retry_interval_s.value_or(this->mod->config.DefaultRetryInterval);
|
||||
|
||||
types::system::LogStatus log_status;
|
||||
if (create_result == DiagnosticsHandler::log_result_t::error_file) {
|
||||
// problem creating the file - nothing to upload
|
||||
log_status.log_status = types::system::LogStatusEnum::UploadFailure;
|
||||
this->publish_log_status(log_status);
|
||||
} else {
|
||||
while (!uploaded && retries <= total_retries && !this->interrupt_log_upload) {
|
||||
retries += 1;
|
||||
log_status.request_id = upload_logs_request.request_id.value_or(-1);
|
||||
run_application(
|
||||
diagnostics_uploader.string(), args, [this, &log_status](const std::string& output_line) {
|
||||
if (output_line == "Uploaded") {
|
||||
log_status.log_status = types::system::string_to_log_status_enum(output_line);
|
||||
} else if (output_line == "UploadFailure" || output_line == "PermissionDenied" ||
|
||||
output_line == "BadMessage" || output_line == "NotSupportedOperation") {
|
||||
log_status.log_status = types::system::LogStatusEnum::UploadFailure;
|
||||
} else {
|
||||
log_status.log_status = types::system::LogStatusEnum::Uploading;
|
||||
}
|
||||
this->publish_log_status(log_status);
|
||||
if (this->interrupt_log_upload) {
|
||||
return CmdControl::Terminate;
|
||||
}
|
||||
return CmdControl::Continue;
|
||||
});
|
||||
if (this->interrupt_log_upload) {
|
||||
EVLOG_info << "Uploading Logs was interrupted, terminating upload script, requestId: "
|
||||
<< log_status.request_id;
|
||||
// N01.FR.20
|
||||
log_status.log_status = types::system::LogStatusEnum::AcceptedCanceled;
|
||||
this->publish_log_status(log_status);
|
||||
} else if (log_status.log_status != types::system::LogStatusEnum::Uploaded &&
|
||||
retries <= total_retries) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(retry_interval));
|
||||
} else {
|
||||
uploaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
this->log_upload_running = false;
|
||||
this->log_upload_cv.notify_one();
|
||||
EVLOG_info << "Log upload thread finished";
|
||||
});
|
||||
this->upload_logs_thread.detach();
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
bool systemImpl::handle_is_reset_allowed(types::system::ResetType& type) {
|
||||
// Allow resets at any time for now
|
||||
return true;
|
||||
}
|
||||
|
||||
void systemImpl::handle_reset(types::system::ResetType& type, bool& scheduled) {
|
||||
if (type == types::system::ResetType::Soft) {
|
||||
EVLOG_info << "Performing soft reset";
|
||||
// This will effectivly stop everest and make it restart via systemd
|
||||
exit(255);
|
||||
} else {
|
||||
EVLOG_info << "Performing hard reset";
|
||||
|
||||
// this reboots the whole linux system
|
||||
system("/sbin/reboot");
|
||||
}
|
||||
}
|
||||
|
||||
bool systemImpl::handle_set_system_time(std::string& timestamp) {
|
||||
// currently not supported, system runs on network time
|
||||
return true;
|
||||
}
|
||||
|
||||
types::system::BootReason systemImpl::handle_get_boot_reason() {
|
||||
return types::system::BootReason::Unknown;
|
||||
}
|
||||
|
||||
void systemImpl::handle_allow_firmware_installation() {
|
||||
EVLOG_info << "Received allow_firmware_installation command - allow firmware update to proceed with reboot.";
|
||||
this->mod->firmware_update_may_proceed_with_reboot_callback();
|
||||
}
|
||||
|
||||
} // namespace main
|
||||
} // namespace module
|
||||
@@ -0,0 +1,76 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef MAIN_SYSTEM_IMPL_HPP
|
||||
#define MAIN_SYSTEM_IMPL_HPP
|
||||
|
||||
//
|
||||
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
// template version 3
|
||||
//
|
||||
|
||||
#include <generated/interfaces/system/Implementation.hpp>
|
||||
|
||||
#include "../Linux_Systemd_Rauc.hpp"
|
||||
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
// insert your custom include headers here
|
||||
#include <filesystem>
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
|
||||
namespace module {
|
||||
namespace main {
|
||||
|
||||
struct Conf {};
|
||||
|
||||
class systemImpl : public systemImplBase {
|
||||
public:
|
||||
systemImpl() = delete;
|
||||
systemImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<Linux_Systemd_Rauc>& mod, Conf& config) :
|
||||
systemImplBase(ev, "main"), mod(mod), config(config){};
|
||||
|
||||
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
|
||||
// insert your public definitions here
|
||||
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
|
||||
|
||||
protected:
|
||||
// command handler functions (virtual)
|
||||
virtual types::system::UpdateFirmwareResponse
|
||||
handle_update_firmware(types::system::FirmwareUpdateRequest& firmware_update_request) override;
|
||||
virtual void handle_allow_firmware_installation() override;
|
||||
virtual types::system::UploadLogsResponse
|
||||
handle_upload_logs(types::system::UploadLogsRequest& upload_logs_request) override;
|
||||
virtual bool handle_is_reset_allowed(types::system::ResetType& type) override;
|
||||
virtual void handle_reset(types::system::ResetType& type, bool& scheduled) override;
|
||||
virtual bool handle_set_system_time(std::string& timestamp) override;
|
||||
virtual types::system::BootReason handle_get_boot_reason() override;
|
||||
|
||||
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
|
||||
// insert your protected definitions here
|
||||
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
|
||||
|
||||
private:
|
||||
const Everest::PtrContainer<Linux_Systemd_Rauc>& mod;
|
||||
const Conf& config;
|
||||
|
||||
virtual void init() override;
|
||||
virtual void ready() override;
|
||||
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
// insert your private definitions here
|
||||
std::filesystem::path scripts_path;
|
||||
bool log_upload_running{false};
|
||||
std::atomic_bool interrupt_log_upload;
|
||||
std::thread upload_logs_thread;
|
||||
std::mutex log_upload_mutex;
|
||||
std::condition_variable log_upload_cv;
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
};
|
||||
|
||||
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
|
||||
// insert other definitions here
|
||||
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
|
||||
|
||||
} // namespace main
|
||||
} // namespace module
|
||||
|
||||
#endif // MAIN_SYSTEM_IMPL_HPP
|
||||
@@ -0,0 +1,45 @@
|
||||
description: This module implements system wide operations for the base linux system
|
||||
config:
|
||||
DefaultRetries:
|
||||
description: Specifies how many times Charge Point tries to upload or download files on previous failure.
|
||||
type: number
|
||||
default: 1
|
||||
DefaultRetryInterval:
|
||||
description: >-
|
||||
Specifies in seconds after which time a retry of an upload or download on previous failure may be attempted.
|
||||
type: number
|
||||
default: 1
|
||||
OCPPLogPath:
|
||||
description: Path to folder where logs of all OCPP messages get written to (see ocpp MessageLogPath)
|
||||
type: string
|
||||
default: /var/log/everest/ocpp
|
||||
SessionLogPath:
|
||||
description: Output directory for session log files (see evse_manager session_logging_path)
|
||||
type: string
|
||||
default: /var/log/everest/session
|
||||
RebootCommand:
|
||||
description: Command to execute for rebooting the system
|
||||
type: string
|
||||
default: /sbin/reboot
|
||||
VerifyUpdateScriptPath:
|
||||
description: >-
|
||||
Full path to shell script that checks if an OTA update has been successful. The script is executed after
|
||||
an update and shall verify the success of the update. It shall return 0 in case of success and
|
||||
non-zero in case of failure.
|
||||
If empty no check is performed and the update is considered successful and it is marked as good.
|
||||
type: string
|
||||
default: ""
|
||||
provides:
|
||||
main:
|
||||
description: Implements the system interface
|
||||
interface: system
|
||||
requires:
|
||||
store:
|
||||
interface: kvs
|
||||
enable_external_mqtt: false
|
||||
metadata:
|
||||
license: https://opensource.org/licenses/Apache-2.0
|
||||
authors:
|
||||
- Piet Gömpel
|
||||
- Cornelius Claussen
|
||||
- James Chapman
|
||||
165
tools/EVerest-main/modules/Misc/Linux_Systemd_Rauc/rauc_dbus.cpp
Normal file
165
tools/EVerest-main/modules/Misc/Linux_Systemd_Rauc/rauc_dbus.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "rauc_dbus.hpp"
|
||||
|
||||
#include <everest/logging.hpp>
|
||||
|
||||
namespace module {
|
||||
using namespace everest::lib::system;
|
||||
|
||||
void Rauc::configure_handlers() {
|
||||
namespace interface = rauc_dbus::interface;
|
||||
namespace property = rauc_dbus::property;
|
||||
namespace signal = rauc_dbus::signal;
|
||||
|
||||
// Subscribe to Complete signal (when install_bundle command finishes)
|
||||
dbus::registerSignalHandler(proxy, interface::Installer, signal::Completed, [this](sdbus::Signal signal) {
|
||||
// Complete signal has one int argument
|
||||
int i;
|
||||
signal >> i;
|
||||
if (update_request_id != request_id_default) {
|
||||
if (i == 0) {
|
||||
EVLOG_info << "RAUC: Installation successful, needs reboot to activate";
|
||||
// Signal to the module code to store the transaction in the database.
|
||||
// We will use this on next boot to signal a Success/Failed Installation
|
||||
signal_store_update_transaction(create_transaction(update_request_id, timeout_us));
|
||||
// The module code should reboot now since we signal InstallRebooting.
|
||||
signal_firmware_update_status(types::system::FirmwareUpdateStatusEnum::InstallRebooting,
|
||||
update_request_id);
|
||||
} else {
|
||||
EVLOG_error << "RAUC: Installation failed with error code: " << i;
|
||||
if (is_installing) {
|
||||
signal_firmware_update_status(types::system::FirmwareUpdateStatusEnum::InstallVerificationFailed,
|
||||
update_request_id);
|
||||
is_installing = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
EVLOG_debug << "RAUC: status from another source";
|
||||
}
|
||||
|
||||
// this was the last message, so reset request_id
|
||||
update_request_id = request_id_default;
|
||||
});
|
||||
|
||||
// Subscribe to property changes std::function<void(Signal signal)>;
|
||||
dbus::registerSignalHandler(
|
||||
proxy, interface::DBus_Properties, signal::PropertiesChanged, [this](sdbus::Signal signal) {
|
||||
// org.freedesktop.DBus.Properties.PropertiesChanged (STRING interface_name,
|
||||
// ARRAY of DICT_ENTRY<STRING,VARIANT>
|
||||
// changed_properties, ARRAY<STRING>
|
||||
// invalidated_properties);
|
||||
std::string interface;
|
||||
signal >> interface;
|
||||
// ignore updates when initiated by someone else
|
||||
if (update_request_id != request_id_default) {
|
||||
if (interface == interface::Installer) {
|
||||
std::map<std::string, sdbus::Variant> changed_properties;
|
||||
signal >> changed_properties;
|
||||
for (const auto& [key, value] : changed_properties) {
|
||||
if (key == property::Progress) {
|
||||
Progress r = value.get<Progress>();
|
||||
EVLOG_info << "Progress " << r.percent << "% " << r.description;
|
||||
|
||||
// Map progress to OCPP structs
|
||||
|
||||
if (r.description.find("Verifying signature done") != std::string::npos) {
|
||||
signal_firmware_update_status(types::system::FirmwareUpdateStatusEnum::Downloaded,
|
||||
update_request_id);
|
||||
signal_firmware_update_status(
|
||||
types::system::FirmwareUpdateStatusEnum::SignatureVerified, update_request_id);
|
||||
signature_verified = true;
|
||||
|
||||
} else if (r.description.find("Verifying signature failed") != std::string::npos) {
|
||||
signal_firmware_update_status(types::system::FirmwareUpdateStatusEnum::Downloaded,
|
||||
update_request_id);
|
||||
signal_firmware_update_status(types::system::FirmwareUpdateStatusEnum::InvalidSignature,
|
||||
update_request_id);
|
||||
signature_verified = true;
|
||||
|
||||
// If bundle checking failed but we never got to signature verification download must
|
||||
// have failed
|
||||
} else if (!signature_verified &&
|
||||
r.description.find("Checking bundle failed") != std::string::npos) {
|
||||
signal_firmware_update_status(types::system::FirmwareUpdateStatusEnum::DownloadFailed,
|
||||
update_request_id);
|
||||
|
||||
} else if (r.description.find("Copying") != std::string::npos &&
|
||||
r.description.find("done") == std::string::npos) {
|
||||
is_installing = true;
|
||||
signal_firmware_update_status(types::system::FirmwareUpdateStatusEnum::Installing,
|
||||
update_request_id);
|
||||
}
|
||||
|
||||
} else if (key == property::Operation) {
|
||||
auto r = rauc_dbus::rauc_messages::string_to_operation(value.get<std::string>());
|
||||
|
||||
if (r == Operation::Idle) {
|
||||
EVLOG_info << "RAUC operation: Idle";
|
||||
} else if (r == Operation::Installing) {
|
||||
EVLOG_info << "RAUC operation: Installing";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> invalidated_properties;
|
||||
signal >> invalidated_properties;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool Rauc::decide_if_good(const rauc_dbus::rauc_messages::UpdateTransaction& saved, const CurrentState& current) {
|
||||
// The original approach uses the primary slot, however this might not be
|
||||
// as reliable as hoped. A change in boot slot should be more reliable
|
||||
// however prior to this change the boot slot wasn't saved
|
||||
|
||||
bool result{false};
|
||||
|
||||
if (RaucBase::decide_if_good(saved, current)) {
|
||||
if (saved.boot_slot.empty()) {
|
||||
// use the previous approach
|
||||
EVLOG_warning << "OTA: fallback to using primary slot";
|
||||
result = saved.primary_slot == current.primary_slot;
|
||||
} else {
|
||||
// use the new approach
|
||||
result = saved.boot_slot != current.boot_slot;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Call on boot and pass a previous transaction that was not closed yet
|
||||
void Rauc::check_previous_transaction(UpdateTransaction t) {
|
||||
signal_remove_update_transaction();
|
||||
|
||||
if (rauc_dbus::RaucBaseSync::check_previous_transaction(t, timeout_us)) {
|
||||
signal_firmware_update_status(types::system::FirmwareUpdateStatusEnum::Installed, t.request_id);
|
||||
} else {
|
||||
signal_firmware_update_status(types::system::FirmwareUpdateStatusEnum::InstallationFailed, t.request_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns immediately. Progress is signalled with signal_firmware_update_status
|
||||
rauc_dbus::rauc_messages::CmdResult Rauc::install_bundle(const std::string& filename, int32_t request_id) {
|
||||
signature_verified = false;
|
||||
is_installing = false;
|
||||
update_request_id = request_id;
|
||||
|
||||
const auto ret = rauc_dbus::RaucBaseSync::install_bundle(filename, timeout_us);
|
||||
if (ret.success) {
|
||||
signal_firmware_update_status(types::system::FirmwareUpdateStatusEnum::Downloading, update_request_id);
|
||||
} else {
|
||||
update_request_id = request_id_default;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Rauc::is_idle() {
|
||||
// Note it is important to query rauc here as well as it may be busy with a local install
|
||||
return (get_operation() == rauc_dbus::rauc_messages::Operation::Idle) && !is_installing;
|
||||
}
|
||||
|
||||
} // namespace module
|
||||
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef RAUC_DBUS_HPP
|
||||
#define RAUC_DBUS_HPP
|
||||
|
||||
#include <everest/system/rauc_dbus_base.hpp>
|
||||
#include <sigslot/signal.hpp>
|
||||
|
||||
#include <generated/types/system.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace module {
|
||||
namespace rauc_dbus = everest::lib::system::rauc_dbus;
|
||||
|
||||
class Rauc : public rauc_dbus::RaucBaseSync {
|
||||
public:
|
||||
// Note OCPP defaults to -1 when not provided
|
||||
static constexpr std::int32_t request_id_default = 0;
|
||||
|
||||
private:
|
||||
constexpr static std::uint64_t timeout_us = 10 * 1000 * 1000; // 10 seconds
|
||||
|
||||
std::atomic<int32_t> update_request_id{request_id_default};
|
||||
std::atomic_bool is_installing{false};
|
||||
std::atomic_bool signature_verified{false};
|
||||
|
||||
void configure_handlers() override;
|
||||
bool decide_if_good(const rauc_dbus::rauc_messages::UpdateTransaction& saved, const CurrentState& current) override;
|
||||
|
||||
public:
|
||||
using CmdResult = rauc_dbus::rauc_messages::CmdResult;
|
||||
using UpdateTransaction = rauc_dbus::rauc_messages::UpdateTransaction;
|
||||
using Operation = rauc_dbus::rauc_messages::Operation;
|
||||
using Progress = rauc_dbus::rauc_messages::Progress;
|
||||
|
||||
using rauc_dbus::RaucBaseSync::RaucBaseSync;
|
||||
Rauc(sdbus::dont_run_event_loop_thread_t) = delete;
|
||||
|
||||
void check_previous_transaction(UpdateTransaction t);
|
||||
bool is_idle();
|
||||
|
||||
CmdResult install_bundle(const std::string& filename, int32_t request_id);
|
||||
|
||||
void mark(const std::string& mark_s, const std::string& slot) {
|
||||
rauc_dbus::RaucBaseSync::mark(mark_s, slot, timeout_us);
|
||||
}
|
||||
|
||||
sigslot::signal<types::system::FirmwareUpdateStatusEnum, int32_t> signal_firmware_update_status;
|
||||
// Emitted when installed update is ready for reboot, the transaction needs to be stored persistently. On next boot,
|
||||
// call check_previous_transaction() with this as an argument
|
||||
sigslot::signal<UpdateTransaction> signal_store_update_transaction;
|
||||
sigslot::signal<> signal_remove_update_transaction;
|
||||
};
|
||||
|
||||
} // namespace module
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user