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:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1,37 @@
load("//modules:module.bzl", "cc_everest_module")
IMPLS = [
"main",
]
cc_everest_module(
name = "System",
impls = IMPLS,
deps = [
"//lib:run_application",
"//lib/everest/timer:libtimer",
"@com_github_HowardHinnant_date//:date",
],
)
# Install script files alongside the binary
genrule(
name = "system_scripts",
srcs = [
"constants.env",
"diagnostics_uploader.sh",
"firmware_updater.sh",
"signed_firmware_downloader.sh",
"signed_firmware_installer.sh",
],
outs = [
"System/constants.env",
"System/diagnostics_uploader.sh",
"System/firmware_updater.sh",
"System/signed_firmware_downloader.sh",
"System/signed_firmware_installer.sh",
],
cmd = "mkdir -p $(RULEDIR)/System && " +
"cp $(SRCS) $(RULEDIR)/System/",
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,39 @@
#
# 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
date::date
date::date-tz
everest::timer
everest::run_application
)
# 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
target_compile_features(${MODULE_NAME} PRIVATE cxx_std_17)
install(
PROGRAMS
constants.env
diagnostics_uploader.sh
firmware_updater.sh
signed_firmware_downloader.sh
signed_firmware_installer.sh
DESTINATION "${EVEREST_MODULE_INSTALL_PREFIX}/${MODULE_NAME}"
)
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "System.hpp"
namespace module {
void System::init() {
invoke_init(*p_main);
}
void System::ready() {
invoke_ready(*p_main);
}
} // namespace module

View File

@@ -0,0 +1,67 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef SYSTEM_HPP
#define SYSTEM_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
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {
double DefaultRetries;
double DefaultRetryInterval;
int ResetDelay;
};
class System : public Everest::ModuleBase {
public:
System() = delete;
System(const ModuleInfo& info, std::unique_ptr<systemImplBase> p_main,
std::vector<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::vector<std::unique_ptr<kvsIntf>> r_store;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
// 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
// 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 // SYSTEM_HPP

View File

@@ -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"

View File

@@ -0,0 +1,21 @@
#!/bin/bash
. "${1}"
echo "$UPLOADING"
sleep 2
curl --progress-bar --ssl --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

View File

@@ -0,0 +1,20 @@
.. _everest_modules_handwritten_System:
.. ******
.. System
.. ******
This module implements system wide operations.
Currently this includes the following commands:
- Log Uploads
- Firmware Updates
- Setting of System time
Corresponding variables signal the state of Log Uploads and Firmware Updates.
Integration in EVerest
======================
This module provides implementation for the system interface. It does not require any other modules.

View File

@@ -0,0 +1,21 @@
#!/bin/bash
. "${1}"
echo "$DOWNLOADING"
curl --progress-bar --ssl --connect-timeout "$CONNECTION_TIMEOUT" "${2}" -o "${3}"
curl_exit_code=$?
sleep 2
if [[ $curl_exit_code -eq 0 ]]; then
echo "$DOWNLOADED"
else
echo "$DOWNLOAD_FAILED"
fi
sleep 2
if [[ $curl_exit_code -eq 0 ]]; then
echo "$INSTALLING"
sleep 2
echo "$INSTALLED"
sleep 2
fi

View File

@@ -0,0 +1,475 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "systemImpl.hpp"
#include <chrono>
#include <cstdlib>
#include <fstream>
#include <thread>
#include <vector>
#include <unistd.h>
#include <utils/date.hpp>
#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";
const std::string FIRMWARE_UPDATER = "firmware_updater.sh";
const std::string SIGNED_FIRMWARE_DOWNLOADER = "signed_firmware_downloader.sh";
const std::string SIGNED_FIRMWARE_INSTALLER = "signed_firmware_installer.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_error << "Failed to create temporary file at: " << fn_template;
return {};
}
// close the file descriptor
close(fd);
return fn_template_buffer.data();
}
void systemImpl::init() {
this->scripts_path = mod->info.paths.libexec;
this->log_upload_running = false;
this->firmware_download_running = false;
this->firmware_installation_running = false;
this->standard_firmware_update_running = false;
this->boot_reason_key = "ocpp_boot_reason";
}
void systemImpl::ready() {
}
void systemImpl::standard_firmware_update(const types::system::FirmwareUpdateRequest& firmware_update_request) {
this->standard_firmware_update_running = true;
EVLOG_info << "Starting firmware update";
// create temporary file
const auto date_time = Everest::Date::to_rfc3339(date::utc_clock::now());
const auto firmware_file_path = create_temp_file(fs::temp_directory_path(), "firmware-" + date_time);
if (firmware_file_path.empty()) {
EVLOG_error << "Firmware update ignored, cannot write temporary file.";
publish_firmware_update_status({types::system::FirmwareUpdateStatusEnum::DownloadFailed});
return;
}
const auto constants = this->scripts_path / CONSTANTS;
this->update_firmware_thread = std::thread([this, firmware_update_request, firmware_file_path, constants]() {
const auto firmware_updater = this->scripts_path / FIRMWARE_UPDATER;
const std::vector<std::string> args = {constants.string(), firmware_update_request.location,
firmware_file_path.string()};
int32_t retries = 0;
const auto total_retries = firmware_update_request.retries.value_or(this->mod->config.DefaultRetries);
const auto retry_interval =
firmware_update_request.retry_interval_s.value_or(this->mod->config.DefaultRetryInterval);
auto firmware_status_enum = types::system::FirmwareUpdateStatusEnum::DownloadFailed;
types::system::FirmwareUpdateStatus firmware_status;
firmware_status.request_id = -1;
firmware_status.firmware_update_status = firmware_status_enum;
while (firmware_status.firmware_update_status == types::system::FirmwareUpdateStatusEnum::DownloadFailed &&
retries < total_retries) {
retries += 1;
run_application(firmware_updater.string(), args, [this, &firmware_status](const std::string& output_line) {
firmware_status.firmware_update_status =
types::system::string_to_firmware_update_status_enum(output_line);
this->publish_firmware_update_status(firmware_status);
return CmdControl::Continue;
});
if (firmware_status.firmware_update_status == types::system::FirmwareUpdateStatusEnum::DownloadFailed &&
retries < total_retries) {
std::this_thread::sleep_for(std::chrono::seconds(retry_interval));
}
if (firmware_status.firmware_update_status == types::system::FirmwareUpdateStatusEnum::Installed and
!this->mod->r_store.empty()) {
this->mod->r_store.at(0)->call_store(boot_reason_key,
boot_reason_to_string(types::system::BootReason::FirmwareUpdate));
}
}
this->standard_firmware_update_running = false;
});
this->update_firmware_thread.detach();
}
types::system::UpdateFirmwareResponse
systemImpl::handle_standard_firmware_update(const types::system::FirmwareUpdateRequest& firmware_update_request) {
if (!this->standard_firmware_update_running) {
if (firmware_update_request.retrieve_timestamp.has_value() &&
Everest::Date::from_rfc3339(firmware_update_request.retrieve_timestamp.value()) > date::utc_clock::now()) {
const auto retrieve_timestamp =
Everest::Date::from_rfc3339(firmware_update_request.retrieve_timestamp.value());
this->standard_update_firmware_timer.at(
[this, retrieve_timestamp, firmware_update_request]() {
this->standard_firmware_update(firmware_update_request);
},
retrieve_timestamp);
EVLOG_info << "Download for firmware scheduled for: " << Everest::Date::to_rfc3339(retrieve_timestamp);
} else {
// start download immediately
this->update_firmware_thread = std::thread(
[this, firmware_update_request]() { this->standard_firmware_update(firmware_update_request); });
this->update_firmware_thread.detach();
}
return types::system::UpdateFirmwareResponse::Accepted;
} else {
EVLOG_info << "Not starting firmware update because firmware update process already running";
return types::system::UpdateFirmwareResponse::Rejected;
}
}
types::system::UpdateFirmwareResponse
systemImpl::handle_signed_fimware_update(const types::system::FirmwareUpdateRequest& firmware_update_request) {
if (!firmware_update_request.signing_certificate.has_value()) {
EVLOG_warning << "Signing certificate is missing in FirmwareUpdateRequest";
return types::system::UpdateFirmwareResponse::Rejected;
}
if (!firmware_update_request.signature.has_value()) {
EVLOG_warning << "Signature is missing in FirmwareUpdateRequest";
return types::system::UpdateFirmwareResponse::Rejected;
}
EVLOG_info << "Executing signed firmware update download callback";
if (firmware_update_request.retrieve_timestamp.has_value() &&
Everest::Date::from_rfc3339(firmware_update_request.retrieve_timestamp.value()) > date::utc_clock::now()) {
const auto retrieve_timestamp = Everest::Date::from_rfc3339(firmware_update_request.retrieve_timestamp.value());
this->signed_firmware_update_download_timer.at(
[this, retrieve_timestamp, firmware_update_request]() {
this->download_signed_firmware(firmware_update_request);
},
retrieve_timestamp);
EVLOG_info << "Download for firmware scheduled for: " << Everest::Date::to_rfc3339(retrieve_timestamp);
types::system::FirmwareUpdateStatus firmware_update_status;
firmware_update_status.request_id = firmware_update_request.request_id;
firmware_update_status.firmware_update_status = types::system::FirmwareUpdateStatusEnum::DownloadScheduled;
this->publish_firmware_update_status(firmware_update_status);
} else {
// start download immediately
this->update_firmware_thread =
std::thread([this, firmware_update_request]() { this->download_signed_firmware(firmware_update_request); });
this->update_firmware_thread.detach();
}
if (this->firmware_download_running) {
return types::system::UpdateFirmwareResponse::AcceptedCanceled;
} else if (this->firmware_installation_running) {
return types::system::UpdateFirmwareResponse::Rejected;
} else {
return types::system::UpdateFirmwareResponse::Accepted;
}
}
void systemImpl::download_signed_firmware(const types::system::FirmwareUpdateRequest& firmware_update_request) {
if (!firmware_update_request.signing_certificate.has_value()) {
EVLOG_warning << "Signing certificate is missing in FirmwareUpdateRequest";
this->publish_firmware_update_status(
{types::system::FirmwareUpdateStatusEnum::DownloadFailed, firmware_update_request.request_id});
return;
}
if (!firmware_update_request.signature.has_value()) {
EVLOG_warning << "Signature is missing in FirmwareUpdateRequest";
this->publish_firmware_update_status(
{types::system::FirmwareUpdateStatusEnum::DownloadFailed, firmware_update_request.request_id});
return;
}
if (this->firmware_download_running) {
EVLOG_info
<< "Received Firmware update request and firmware update already running - cancelling firmware update";
this->interrupt_firmware_download.exchange(true);
EVLOG_info << "Waiting for other firmware download to finish...";
std::unique_lock<std::mutex> lk(this->firmware_update_mutex);
this->firmware_update_cv.wait(lk, [this]() { return !this->firmware_download_running; });
EVLOG_info << "Previous Firmware download finished!";
}
std::lock_guard<std::mutex> lg(this->firmware_update_mutex);
EVLOG_info << "Starting Firmware update";
this->interrupt_firmware_download.exchange(false);
this->firmware_download_running = true;
// // create temporary file
const auto date_time = Everest::Date::to_rfc3339(date::utc_clock::now());
const auto firmware_file_path = create_temp_file(fs::temp_directory_path(), "signed_firmware-" + date_time);
const auto firmware_downloader = this->scripts_path / SIGNED_FIRMWARE_DOWNLOADER;
const auto constants = this->scripts_path / CONSTANTS;
const std::vector<std::string> download_args = {
constants.string(), firmware_update_request.location, firmware_file_path.string(),
firmware_update_request.signature.value(), firmware_update_request.signing_certificate.value()};
int32_t retries = 0;
const auto total_retries = firmware_update_request.retries.value_or(this->mod->config.DefaultRetries);
const auto retry_interval =
firmware_update_request.retry_interval_s.value_or(this->mod->config.DefaultRetryInterval);
auto firmware_status_enum = types::system::FirmwareUpdateStatusEnum::DownloadFailed;
types::system::FirmwareUpdateStatus firmware_status;
firmware_status.request_id = firmware_update_request.request_id;
firmware_status.firmware_update_status = firmware_status_enum;
while (firmware_status.firmware_update_status == types::system::FirmwareUpdateStatusEnum::DownloadFailed &&
retries < total_retries && !this->interrupt_firmware_download) {
run_application(
firmware_downloader.string(), download_args, [this, &firmware_status](const std::string& output_line) {
firmware_status.firmware_update_status =
types::system::string_to_firmware_update_status_enum(output_line);
this->publish_firmware_update_status(firmware_status);
if (this->interrupt_firmware_download) {
EVLOG_info << "Updating firmware was interrupted, terminating firmware update script, requestId: "
<< firmware_status.request_id;
return CmdControl::Terminate;
}
return CmdControl::Continue;
});
retries += 1;
if (firmware_status.firmware_update_status == types::system::FirmwareUpdateStatusEnum::DownloadFailed &&
retries < total_retries) {
std::this_thread::sleep_for(std::chrono::seconds(retry_interval));
}
}
if (firmware_status.firmware_update_status == types::system::FirmwareUpdateStatusEnum::SignatureVerified) {
this->initialize_firmware_installation(firmware_update_request, firmware_file_path);
}
this->firmware_download_running = false;
this->firmware_update_cv.notify_one();
EVLOG_info << "Firmware update thread finished";
}
void systemImpl::initialize_firmware_installation(const types::system::FirmwareUpdateRequest& firmware_update_request,
const fs::path& firmware_file_path) {
if (firmware_update_request.install_timestamp.has_value() &&
Everest::Date::from_rfc3339(firmware_update_request.install_timestamp.value()) > date::utc_clock::now()) {
const auto install_timestamp = Everest::Date::from_rfc3339(firmware_update_request.install_timestamp.value());
this->signed_firmware_update_install_timer.at(
[this, firmware_update_request, firmware_file_path]() {
this->install_signed_firmware(firmware_update_request, firmware_file_path);
},
install_timestamp);
EVLOG_info << "Installation for firmware scheduled for: " << Everest::Date::to_rfc3339(install_timestamp);
types::system::FirmwareUpdateStatus firmware_update_status;
firmware_update_status.request_id = firmware_update_request.request_id;
firmware_update_status.firmware_update_status = types::system::FirmwareUpdateStatusEnum::InstallScheduled;
this->publish_firmware_update_status(firmware_update_status);
} else {
// start installation immediately
this->update_firmware_thread = std::thread([this, firmware_update_request, firmware_file_path]() {
this->install_signed_firmware(firmware_update_request, firmware_file_path);
});
this->update_firmware_thread.detach();
}
}
void systemImpl::install_signed_firmware(const types::system::FirmwareUpdateRequest& firmware_update_request,
const fs::path& firmware_file_path) {
auto firmware_status_enum = types::system::FirmwareUpdateStatusEnum::Installing;
types::system::FirmwareUpdateStatus firmware_status;
firmware_status.request_id = firmware_update_request.request_id;
firmware_status.firmware_update_status = firmware_status_enum;
if (!this->firmware_installation_running) {
this->firmware_installation_running = true;
const auto firmware_installer = this->scripts_path / SIGNED_FIRMWARE_INSTALLER;
const auto constants = this->scripts_path / CONSTANTS;
const std::vector<std::string> install_args = {constants.string()};
run_application(firmware_installer.string(), install_args,
[this, &firmware_status](const std::string& output_line) {
firmware_status.firmware_update_status =
types::system::string_to_firmware_update_status_enum(output_line);
this->publish_firmware_update_status(firmware_status);
return CmdControl::Continue;
});
if (firmware_status.firmware_update_status == types::system::FirmwareUpdateStatusEnum::Installed) {
if (!this->mod->r_store.empty()) {
this->mod->r_store.at(0)->call_store(boot_reason_key,
boot_reason_to_string(types::system::BootReason::FirmwareUpdate));
}
auto reset_type = types::system::ResetType::Hard;
bool firmware_installation_running_copy = this->firmware_installation_running;
this->handle_reset(reset_type, firmware_installation_running_copy);
}
} else {
firmware_status.firmware_update_status = types::system::FirmwareUpdateStatusEnum::InstallationFailed;
this->publish_firmware_update_status(firmware_status);
}
}
types::system::UpdateFirmwareResponse
systemImpl::handle_update_firmware(types::system::FirmwareUpdateRequest& firmware_update_request) {
if (firmware_update_request.request_id == -1) {
return this->handle_standard_firmware_update(firmware_update_request);
} else {
return this->handle_signed_fimware_update(firmware_update_request);
}
};
void systemImpl::handle_allow_firmware_installation() {
// TODO: implement me
}
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());
// TODO(piet): consider start time and end time
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.file_name = diagnostics_file_name;
const auto fake_diagnostics_file = json({{"diagnostics", {{"key", "value"}}}});
std::ofstream diagnostics_file(diagnostics_file_path.c_str());
diagnostics_file << fake_diagnostics_file.dump();
this->upload_logs_thread = std::thread([this, 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;
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) {
// command finished, but neither interrupted nor uploaded
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) {
// Right now we dont want to reject a reset ever
return true;
}
void systemImpl::handle_reset(types::system::ResetType& type, bool& scheduled) {
// let the actual work be done by a worker thread, which can also delay it
// a little bit (if configured) to allow e.g. clean shutdown of communication
// channels in parallel when this call returns
std::thread([this, type, scheduled] {
EVLOG_info << "Reset request received: " << type << ", " << (scheduled ? "" : "not ") << "scheduled";
if (!this->mod->r_store.empty() and !this->mod->r_store.at(0)->call_exists(boot_reason_key)) {
this->mod->r_store.at(0)->call_store(boot_reason_key,
boot_reason_to_string(types::system::BootReason::RemoteReset));
}
std::this_thread::sleep_for(std::chrono::seconds(this->mod->config.ResetDelay));
if (type == types::system::ResetType::Soft) {
EVLOG_info << "Performing soft reset now.";
kill(getpid(), SIGINT);
} else {
EVLOG_info << "Performing hard reset now.";
kill(getpid(), SIGINT); // FIXME(piet): Define appropriate behavior for hard reset
}
}).detach();
}
bool systemImpl::handle_set_system_time(std::string& timestamp) {
// your code for cmd set_system_time goes here
return true;
};
types::system::BootReason systemImpl::handle_get_boot_reason() {
if (this->mod->r_store.empty()) {
return types::system::BootReason::PowerUp;
}
auto reason_variant = this->mod->r_store.at(0)->call_load(boot_reason_key);
auto* reason = std::get_if<std::string>(&reason_variant);
std::string final_reason{boot_reason_to_string(types::system::BootReason::PowerUp)};
if (reason != nullptr) {
final_reason = *reason;
}
this->mod->r_store.at(0)->call_delete(boot_reason_key);
return types::system::string_to_boot_reason(final_reason);
}
} // namespace main
} // namespace module

View File

@@ -0,0 +1,152 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 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 "../System.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
#include <filesystem>
#include <everest/timer.hpp>
// 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<System>& 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<System>& 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;
std::atomic<bool> interrupt_firmware_download;
std::atomic<bool> interrupt_log_upload;
bool log_upload_running;
bool standard_firmware_update_running;
bool firmware_download_running;
std::atomic<bool> firmware_installation_running;
std::condition_variable log_upload_cv;
std::condition_variable firmware_update_cv;
std::mutex log_upload_mutex;
std::mutex firmware_update_mutex;
std::thread update_firmware_thread;
std::thread upload_logs_thread;
Everest::SteadyTimer standard_update_firmware_timer;
Everest::SteadyTimer signed_firmware_update_download_timer;
Everest::SteadyTimer signed_firmware_update_install_timer;
std::string boot_reason_key;
/**
* @brief Executes a standard firmware update using the given \p firmware_update_request
*
* @param firmware_update_request
*/
void standard_firmware_update(const types::system::FirmwareUpdateRequest& firmware_update_request);
/**
* @brief Handles the given \p firmware_update_request . If firmware update is already running, the request will be
* rejected. If the download should not be started in the future it starts the download and installation of the
* firmware immediately, otherwise this method sets a timer for the download accordingly.
*
* @param firmware_update_request
* @return types::system::UpdateFirmwareResponse
*/
types::system::UpdateFirmwareResponse
handle_standard_firmware_update(const types::system::FirmwareUpdateRequest& firmware_update_request);
/**
* @brief Handles the given \p firmware_update_request. If the download should not be started in the future it
* starts the download and installation of the firmware immediately, otherwise this method sets a timer for the
* download accordingly.
*
* @param firmware_update_request
*/
types::system::UpdateFirmwareResponse
handle_signed_fimware_update(const types::system::FirmwareUpdateRequest& firmware_update_request);
/**
* @brief Handles the download of the firmware specified in the given \p firmware_update_request . If a download is
* already running, this method will interrupt the download process and restart it.
*
* @param firmware_update_request
*/
void download_signed_firmware(const types::system::FirmwareUpdateRequest& firmware_update_request);
/**
* @brief Initializes the firmware installation by starting it immediately or if specified in the \p
* firmware_update_request it schedules it for the future.
*
* @param firmware_update_request
* @param firmware_file_path
*/
void initialize_firmware_installation(const types::system::FirmwareUpdateRequest& firmware_update_request,
const std::filesystem::path& firmware_file_path);
/**
* @brief Executes the installation of the firmware specified in the given \p firmware_update_request .
*
* @param firmware_update_reqeust
* @param firmware_file_path
*/
void install_signed_firmware(const types::system::FirmwareUpdateRequest& firmware_update_reqeust,
const std::filesystem::path& firmware_file_path);
// 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

View File

@@ -0,0 +1,33 @@
description: This module implements system wide operations
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
ResetDelay:
description: >-
When receiving a reset request, then the actual execution can be delayed by this amount of time (given in seconds).
This might be necessary, for example, when the reset request arrives via the network and the call acknowledgement
should be given some time to travel the return path to the caller.
Defaults to zero, which means that the reset is executed directly without delay.
type: integer
minimum: 0
default: 0
provides:
main:
description: Implements the system interface
interface: system
requires:
store:
interface: kvs
min_connections: 0
max_connections: 1
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Piet Gömpel

View File

@@ -0,0 +1,30 @@
#!/bin/bash
. "${1}"
SIGNATURE_VALIDATION_DIR=$(mktemp -d /tmp/signature_validation_XXXXX)
sleep 2
echo "$DOWNLOADING"
sleep 2
curl --progress-bar --ssl --connect-timeout "$CONNECTION_TIMEOUT" "${2}" -o "${3}"
curl_exit_code=$?
sleep 2
if [[ $curl_exit_code -eq 0 ]]; then
echo "$DOWNLOADED"
echo -e "${4}" >"$SIGNATURE_VALIDATION_DIR/firmware_signature.base64"
echo -e "${5}" >"$SIGNATURE_VALIDATION_DIR/firmware_cert.pem"
openssl x509 -pubkey -noout -in "$SIGNATURE_VALIDATION_DIR/firmware_cert.pem" >"$SIGNATURE_VALIDATION_DIR/pubkey.pem"
openssl base64 -d -in "$SIGNATURE_VALIDATION_DIR/firmware_signature.base64" -out "$SIGNATURE_VALIDATION_DIR/firmware_signature.sha256"
r=$(openssl dgst -sha256 -verify "$SIGNATURE_VALIDATION_DIR/pubkey.pem" -signature "$SIGNATURE_VALIDATION_DIR/firmware_signature.sha256" "${3}")
if [ "$r" = "Verified OK" ]; then
echo "$SIGNATURE_VERIFIED"
else
echo "$INVALID_SIGNATURE"
fi
else
echo "$DOWNLOAD_FAILED"
fi
rm -rf "$SIGNATURE_VALIDATION_DIR"

View File

@@ -0,0 +1,7 @@
#!/bin/bash
. "${1}"
echo "$INSTALLING"
sleep 2
echo "$INSTALLED"