From 6b38f3210efad964b2bde16bf9361fcc05c1839e Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Tue, 12 Dec 2023 19:08:19 +0100 Subject: [PATCH] Add chip-tool BDXTransferServerDelegate support --- examples/chip-tool/BUILD.gn | 1 + .../commands/clusters/ClusterCommand.h | 18 ++ .../BDXDiagnosticLogsServerDelegate.cpp | 192 ++++++++++++++++++ .../common/BDXDiagnosticLogsServerDelegate.h | 45 ++++ .../chip-tool/commands/common/CHIPCommand.cpp | 8 + .../chip-tool/commands/common/CHIPCommand.h | 2 + .../tv-casting-app/tv-casting-common/BUILD.gn | 1 + 7 files changed, 267 insertions(+) create mode 100644 examples/chip-tool/commands/common/BDXDiagnosticLogsServerDelegate.cpp create mode 100644 examples/chip-tool/commands/common/BDXDiagnosticLogsServerDelegate.h diff --git a/examples/chip-tool/BUILD.gn b/examples/chip-tool/BUILD.gn index 53568316114c9a..c6f5edc819ba95 100644 --- a/examples/chip-tool/BUILD.gn +++ b/examples/chip-tool/BUILD.gn @@ -55,6 +55,7 @@ static_library("chip-tool-utils") { "${chip_root}/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp", "commands/clusters/ModelCommand.cpp", "commands/clusters/ModelCommand.h", + "commands/common/BDXDiagnosticLogsServerDelegate.cpp", "commands/common/CHIPCommand.cpp", "commands/common/CHIPCommand.h", "commands/common/Command.cpp", diff --git a/examples/chip-tool/commands/clusters/ClusterCommand.h b/examples/chip-tool/commands/clusters/ClusterCommand.h index 6b68e1b04bfea0..792588a886dffb 100644 --- a/examples/chip-tool/commands/clusters/ClusterCommand.h +++ b/examples/chip-tool/commands/clusters/ClusterCommand.h @@ -55,6 +55,22 @@ class ClusterCommand : public InteractionModelCommands, public ModelCommand, pub return InteractionModelCommands::SendCommand(device, endpointId, clusterId, commandId, value); } + CHIP_ERROR SendCommand(chip::DeviceProxy * device, chip::EndpointId endpointId, chip::ClusterId clusterId, + chip::CommandId commandId, + const chip::app::Clusters::DiagnosticLogs::Commands::RetrieveLogsRequest::Type & value) + { + ReturnErrorOnFailure(InteractionModelCommands::SendCommand(device, endpointId, clusterId, commandId, value)); + + if (value.transferFileDesignator.HasValue() && + value.requestedProtocol == chip::app::Clusters::DiagnosticLogs::TransferProtocolEnum::kBdx) + { + auto sender = mCommandSender.back().get(); + auto fileDesignator = value.transferFileDesignator.Value(); + BDXDiagnosticLogsServerDelegate::GetInstance().AddFileDesignator(sender, fileDesignator); + } + return CHIP_NO_ERROR; + } + CHIP_ERROR SendGroupCommand(chip::GroupId groupId, chip::FabricIndex fabricIndex) override { return InteractionModelCommands::SendGroupCommand(groupId, fabricIndex, mClusterId, mCommandId, mPayload); @@ -120,6 +136,8 @@ class ClusterCommand : public InteractionModelCommands, public ModelCommand, pub shouldStop = mRepeatCount.Value() == 0; } + BDXDiagnosticLogsServerDelegate::GetInstance().RemoveFileDesignator(client); + if (shouldStop) { SetCommandExitStatus(mError); diff --git a/examples/chip-tool/commands/common/BDXDiagnosticLogsServerDelegate.cpp b/examples/chip-tool/commands/common/BDXDiagnosticLogsServerDelegate.cpp new file mode 100644 index 00000000000000..c4bda1874fc9c3 --- /dev/null +++ b/examples/chip-tool/commands/common/BDXDiagnosticLogsServerDelegate.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "BDXDiagnosticLogsServerDelegate.h" + +#include + +constexpr const char kTmpDir[] = "/tmp/"; +constexpr uint8_t kMaxFileDesignatorLen = 32; +constexpr uint16_t kMaxFilePathLen = kMaxFileDesignatorLen + sizeof(kTmpDir) + 1; + +// For testing a few file names trigger an error depending on the current 'phase'. +constexpr char kErrorOnTransferBegin[] = "Error:OnTransferBegin"; +constexpr char kErrorOnTransferData[] = "Error:OnTransferData"; +constexpr char kErrorOnTransferEnd[] = "Error:OnTransferEnd"; + +BDXDiagnosticLogsServerDelegate BDXDiagnosticLogsServerDelegate::sInstance; + +CHIP_ERROR CheckForErrorRequested(const chip::CharSpan & phaseErrorTarget, const chip::CharSpan & fileDesignator) +{ + VerifyOrReturnError(!phaseErrorTarget.data_equal(fileDesignator), CHIP_ERROR_INTERNAL); + return CHIP_NO_ERROR; +} + +CHIP_ERROR CheckFileDesignatorAllowed(const std::map & fileDesignators, + const chip::CharSpan & fileDesignator) +{ + std::string targetFileDesignator(fileDesignator.data(), fileDesignator.size()); + + for (auto it = fileDesignators.begin(); it != fileDesignators.end(); it++) + { + auto expectedFileDesignator = it->second; + if (targetFileDesignator.compare(expectedFileDesignator) == 0) + { + return CHIP_NO_ERROR; + } + } + + return CHIP_ERROR_NOT_FOUND; +} + +CHIP_ERROR GetFilePath(const chip::CharSpan & fileDesignator, chip::MutableCharSpan & outFilePath) +{ + VerifyOrReturnError(fileDesignator.size() <= kMaxFileDesignatorLen, CHIP_ERROR_INVALID_STRING_LENGTH); + + memcpy(outFilePath.data(), kTmpDir, sizeof(kTmpDir)); + memcpy(outFilePath.data() + sizeof(kTmpDir) - 1, fileDesignator.data(), fileDesignator.size()); + outFilePath.reduce_size(sizeof(kTmpDir) + fileDesignator.size()); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR CheckFileExists(const char * filePath) +{ + if (access(filePath, F_OK) != 0) + { + ChipLogError(chipTool, "The file '%s' for dumping the logs does not exist.", filePath); + return CHIP_ERROR_INCORRECT_STATE; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR CheckFileDoesNotExist(const char * filePath) +{ + if (access(filePath, F_OK) == 0) + { + ChipLogError(chipTool, "The file '%s' for dumping the logs already exists.", filePath); + return CHIP_ERROR_INCORRECT_STATE; + } + + return CHIP_NO_ERROR; +} + +void LogFileDesignator(const char * prefix, const chip::CharSpan & fileDesignator, CHIP_ERROR error = CHIP_NO_ERROR) +{ +#if CHIP_PROGRESS_LOGGING + auto size = static_cast(fileDesignator.size()); + auto data = fileDesignator.data(); + ChipLogProgress(chipTool, "%s (%u): %.*s", prefix, size, size, data); +#endif // CHIP_PROGRESS_LOGGING + + if (CHIP_NO_ERROR != error) + { + ChipLogError(chipTool, "%s: %s", prefix, ErrorStr(error)); + } +} + +CHIP_ERROR CreateFile(const char * filePath) +{ + VerifyOrReturnError(nullptr != filePath, CHIP_ERROR_INVALID_ARGUMENT); + + auto fd = fopen(filePath, "w+"); + VerifyOrReturnError(nullptr != fd, CHIP_ERROR_WRITE_FAILED); + + auto rv = fclose(fd); + VerifyOrReturnError(EOF != rv, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR AppendToFile(const char * filePath, const chip::ByteSpan & data) +{ + VerifyOrReturnError(nullptr != filePath, CHIP_ERROR_INVALID_ARGUMENT); + + auto fd = fopen(filePath, "a"); + VerifyOrReturnError(nullptr != fd, CHIP_ERROR_WRITE_FAILED); + + fwrite(data.data(), data.size(), 1, fd); + + auto rv = fclose(fd); + VerifyOrReturnError(EOF != rv, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR BDXDiagnosticLogsServerDelegate::OnTransferBegin(chip::bdx::BDXTransferProxy * transfer) +{ + auto fileDesignator = transfer->GetFileDesignator(); + LogFileDesignator("OnTransferBegin", fileDesignator); + + chip::CharSpan phaseErrorTarget(kErrorOnTransferBegin, sizeof(kErrorOnTransferBegin) - 1); + ReturnErrorOnFailure(CheckForErrorRequested(phaseErrorTarget, fileDesignator)); + ReturnErrorOnFailure(CheckFileDesignatorAllowed(mFileDesignators, fileDesignator)); + + char outputFilePath[kMaxFilePathLen] = { 0 }; + chip::MutableCharSpan outputFilePathSpan(outputFilePath); + ReturnErrorOnFailure(GetFilePath(fileDesignator, outputFilePathSpan)); + ReturnErrorOnFailure(CheckFileDoesNotExist(outputFilePath)); + ReturnErrorOnFailure(CreateFile(outputFilePath)); + + return transfer->Accept(); +} + +CHIP_ERROR BDXDiagnosticLogsServerDelegate::OnTransferEnd(chip::bdx::BDXTransferProxy * transfer, CHIP_ERROR error) +{ + auto fileDesignator = transfer->GetFileDesignator(); + LogFileDesignator("OnTransferEnd", fileDesignator, error); + + chip::CharSpan phaseErrorTarget(kErrorOnTransferEnd, sizeof(kErrorOnTransferEnd) - 1); + ReturnErrorOnFailure(CheckForErrorRequested(phaseErrorTarget, fileDesignator)); + + char outputFilePath[kMaxFilePathLen] = { 0 }; + chip::MutableCharSpan outputFilePathSpan(outputFilePath); + ReturnErrorOnFailure(GetFilePath(fileDesignator, outputFilePathSpan)); + ReturnErrorOnFailure(CheckFileExists(outputFilePath)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR BDXDiagnosticLogsServerDelegate::OnTransferData(chip::bdx::BDXTransferProxy * transfer, const chip::ByteSpan & data) +{ + auto fileDesignator = transfer->GetFileDesignator(); + LogFileDesignator("OnTransferData", fileDesignator); + + chip::CharSpan phaseErrorTarget(kErrorOnTransferData, sizeof(kErrorOnTransferData) - 1); + ReturnErrorOnFailure(CheckForErrorRequested(phaseErrorTarget, fileDesignator)); + + char outputFilePath[kMaxFilePathLen] = { 0 }; + chip::MutableCharSpan outputFilePathSpan(outputFilePath); + ReturnErrorOnFailure(GetFilePath(fileDesignator, outputFilePathSpan)); + ReturnErrorOnFailure(CheckFileExists(outputFilePath)); + ReturnErrorOnFailure(AppendToFile(outputFilePath, data)); + + return transfer->Continue(); +} + +void BDXDiagnosticLogsServerDelegate::AddFileDesignator(chip::app::CommandSender * sender, const chip::CharSpan & fileDesignator) +{ + std::string entry(fileDesignator.data(), fileDesignator.size()); + mFileDesignators[sender] = entry; +} + +void BDXDiagnosticLogsServerDelegate::RemoveFileDesignator(chip::app::CommandSender * sender) +{ + mFileDesignators.erase(sender); +} diff --git a/examples/chip-tool/commands/common/BDXDiagnosticLogsServerDelegate.h b/examples/chip-tool/commands/common/BDXDiagnosticLogsServerDelegate.h new file mode 100644 index 00000000000000..5aea4b56632e79 --- /dev/null +++ b/examples/chip-tool/commands/common/BDXDiagnosticLogsServerDelegate.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once + +#include +#include + +#include +#include + +class BDXDiagnosticLogsServerDelegate : public chip::bdx::BDXTransferServerDelegate +{ +public: + static BDXDiagnosticLogsServerDelegate & GetInstance() { return sInstance; } + + void AddFileDesignator(chip::app::CommandSender * sender, const chip::CharSpan & fileDesignator); + void RemoveFileDesignator(chip::app::CommandSender * sender); + + /////////// BDXTransferServerDelegate Interface ///////// + CHIP_ERROR OnTransferBegin(chip::bdx::BDXTransferProxy * transfer) override; + CHIP_ERROR OnTransferEnd(chip::bdx::BDXTransferProxy * transfer, CHIP_ERROR error) override; + CHIP_ERROR OnTransferData(chip::bdx::BDXTransferProxy * transfer, const chip::ByteSpan & data) override; + +private: + BDXDiagnosticLogsServerDelegate() = default; + + std::map mFileDesignators; + static BDXDiagnosticLogsServerDelegate sInstance; +}; diff --git a/examples/chip-tool/commands/common/CHIPCommand.cpp b/examples/chip-tool/commands/common/CHIPCommand.cpp index 45fc2105183cdc..78abff12ada208 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.cpp +++ b/examples/chip-tool/commands/common/CHIPCommand.cpp @@ -139,6 +139,14 @@ CHIP_ERROR CHIPCommand::MaybeSetUpStack() factoryInitParams.listenPort = port; ReturnLogErrorOnFailure(DeviceControllerFactory::GetInstance().Init(factoryInitParams)); + auto systemState = chip::Controller::DeviceControllerFactory::GetInstance().GetSystemState(); + VerifyOrReturnError(nullptr != systemState, CHIP_ERROR_INCORRECT_STATE); + + auto server = systemState->BDXTransferServer(); + VerifyOrReturnError(nullptr != server, CHIP_ERROR_INCORRECT_STATE); + + server->SetDelegate(&BDXDiagnosticLogsServerDelegate::GetInstance()); + ReturnErrorOnFailure(GetAttestationTrustStore(mPaaTrustStorePath.ValueOr(nullptr), &sTrustStore)); ReturnLogErrorOnFailure(sCheckInDelegate.Init(&sICDClientStorage)); diff --git a/examples/chip-tool/commands/common/CHIPCommand.h b/examples/chip-tool/commands/common/CHIPCommand.h index 2caf6fe9cc94ec..55817b5213e589 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.h +++ b/examples/chip-tool/commands/common/CHIPCommand.h @@ -24,6 +24,8 @@ #include "Command.h" +#include "BDXDiagnosticLogsServerDelegate.h" + #include #include #include diff --git a/examples/tv-casting-app/tv-casting-common/BUILD.gn b/examples/tv-casting-app/tv-casting-common/BUILD.gn index d9c391b1e1b037..4d7d21469065af 100644 --- a/examples/tv-casting-app/tv-casting-common/BUILD.gn +++ b/examples/tv-casting-app/tv-casting-common/BUILD.gn @@ -39,6 +39,7 @@ chip_data_model("tv-casting-common") { sources = [ "${chip_root}/examples/chip-tool/commands/clusters/ModelCommand.h", + "${chip_root}/examples/chip-tool/commands/common/BDXDiagnosticLogsServerDelegate.cpp", "${chip_root}/examples/chip-tool/commands/common/CHIPCommand.h", "${chip_root}/examples/chip-tool/commands/common/Command.cpp", "${chip_root}/examples/chip-tool/commands/common/Command.h",