blob: 95d9a332819ae33e348a4435e2e957f1a3536453 [file] [log] [blame]
/**
*
* Copyright (c) 2021 Project CHIP Authors
*
* 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 <app/clusters/diagnostic-logs-server/diagnostic-logs-server.h>
#include <app/util/af.h>
#include <app/util/config.h>
#include <lib/support/ScopedBuffer.h>
#include <protocols/bdx/DiagnosticLogs.h>
#include "BDXDiagnosticLogsProvider.h"
#ifdef MATTER_DM_DIAGNOSTIC_LOGS_CLUSTER_SERVER_ENDPOINT_COUNT
static constexpr size_t kDiagnosticLogsDiagnosticLogsProviderDelegateTableSize =
MATTER_DM_DIAGNOSTIC_LOGS_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT;
static_assert(kDiagnosticLogsDiagnosticLogsProviderDelegateTableSize < kEmberInvalidEndpointIndex,
"DiagnosticLogs: log provider delegate table size error");
using namespace chip::app::Clusters::DiagnosticLogs;
using chip::Protocols::InteractionModel::Status;
using chip::bdx::DiagnosticLogs::kMaxFileDesignatorLen;
using chip::bdx::DiagnosticLogs::kMaxLogContentSize;
namespace chip {
namespace app {
namespace Clusters {
namespace DiagnosticLogs {
namespace {
DiagnosticLogsProviderDelegate * gDiagnosticLogsProviderDelegateTable[kDiagnosticLogsDiagnosticLogsProviderDelegateTableSize] = {
nullptr
};
DiagnosticLogsProviderDelegate * GetDiagnosticLogsProviderDelegate(EndpointId endpoint)
{
uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, Id, MATTER_DM_DIAGNOSTIC_LOGS_CLUSTER_SERVER_ENDPOINT_COUNT);
auto delegate = (ep >= ArraySize(gDiagnosticLogsProviderDelegateTable) ? nullptr : gDiagnosticLogsProviderDelegateTable[ep]);
if (delegate == nullptr)
{
ChipLogProgress(Zcl, "Diagnosticlogs: no log provider delegate set for endpoint:%u", endpoint);
}
return delegate;
}
void AddResponse(CommandHandler * commandObj, const ConcreteCommandPath & path, StatusEnum status)
{
Commands::RetrieveLogsResponse::Type response;
response.status = status;
commandObj->AddResponse(path, response);
}
void AddResponse(CommandHandler * commandObj, const ConcreteCommandPath & path, StatusEnum status, MutableByteSpan & logContent,
const Optional<uint64_t> & timeStamp, const Optional<uint64_t> & timeSinceBoot)
{
Commands::RetrieveLogsResponse::Type response;
response.status = status;
response.logContent = ByteSpan(logContent);
response.UTCTimeStamp = timeStamp;
response.timeSinceBoot = timeSinceBoot;
commandObj->AddResponse(path, response);
}
#if CHIP_CONFIG_ENABLE_BDX_LOG_TRANSFER
BDXDiagnosticLogsProvider gBDXDiagnosticLogsProvider;
#endif // CHIP_CONFIG_ENABLE_BDX_LOG_TRANSFER
} // anonymous namespace
DiagnosticLogsServer DiagnosticLogsServer::sInstance;
void DiagnosticLogsServer::SetDiagnosticLogsProviderDelegate(EndpointId endpoint, DiagnosticLogsProviderDelegate * delegate)
{
uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, Id, MATTER_DM_DIAGNOSTIC_LOGS_CLUSTER_SERVER_ENDPOINT_COUNT);
if (ep < kDiagnosticLogsDiagnosticLogsProviderDelegateTableSize)
{
gDiagnosticLogsProviderDelegateTable[ep] = delegate;
}
}
DiagnosticLogsServer & DiagnosticLogsServer::Instance()
{
return sInstance;
}
void DiagnosticLogsServer::HandleLogRequestForResponsePayload(CommandHandler * commandObj, const ConcreteCommandPath & path,
IntentEnum intent, StatusEnum status)
{
// If there is no delegate, there is no mechanism to read the logs. Assume those are empty and return NoLogs
auto * delegate = GetDiagnosticLogsProviderDelegate(path.mEndpointId);
VerifyOrReturn(nullptr != delegate, AddResponse(commandObj, path, StatusEnum::kNoLogs));
Platform::ScopedMemoryBuffer<uint8_t> buffer;
VerifyOrReturn(buffer.Alloc(kMaxLogContentSize), AddResponse(commandObj, path, StatusEnum::kDenied));
auto logContent = MutableByteSpan(buffer.Get(), kMaxLogContentSize);
Optional<uint64_t> timeStamp;
Optional<uint64_t> timeSinceBoot;
auto err = delegate->GetLogForIntent(intent, logContent, timeStamp, timeSinceBoot);
VerifyOrReturn(CHIP_ERROR_NOT_FOUND != err, AddResponse(commandObj, path, StatusEnum::kNoLogs));
VerifyOrReturn(CHIP_NO_ERROR == err, AddResponse(commandObj, path, StatusEnum::kDenied));
AddResponse(commandObj, path, status, logContent, timeStamp, timeSinceBoot);
}
void DiagnosticLogsServer::HandleLogRequestForBdx(CommandHandler * commandObj, const ConcreteCommandPath & path, IntentEnum intent,
Optional<CharSpan> transferFileDesignator)
{
// If the RequestedProtocol is set to BDX and there is no TransferFileDesignator the command SHALL fail with a Status Code of
// INVALID_COMMAND.
VerifyOrReturn(transferFileDesignator.HasValue(), commandObj->AddStatus(path, Status::InvalidCommand));
VerifyOrReturn(transferFileDesignator.Value().size() <= kMaxFileDesignatorLen,
commandObj->AddStatus(path, Status::ConstraintError));
// If there is no delegate, there is no mechanism to read the logs. Assume those are empty and return NoLogs
auto * delegate = GetDiagnosticLogsProviderDelegate(path.mEndpointId);
VerifyOrReturn(nullptr != delegate, AddResponse(commandObj, path, StatusEnum::kNoLogs));
// In the case where the Node is able to fit the entirety of the requested logs within the LogContent field, the Status field of
// the RetrieveLogsResponse SHALL be set to Exhausted and a BDX session SHALL NOT be initiated.
VerifyOrReturn(delegate->GetSizeForIntent(intent) > kMaxLogContentSize,
HandleLogRequestForResponsePayload(commandObj, path, intent, StatusEnum::kExhausted));
// If the RequestedProtocol is set to BDX and either the Node does not support BDX or it is not possible for the Node
// to establish a BDX session, then the Node SHALL utilize the LogContent field of the RetrieveLogsResponse command
// to transfer as much of the current logs as it can fit within the response, and the Status field of the
// RetrieveLogsResponse SHALL be set to Exhausted.
#if CHIP_CONFIG_ENABLE_BDX_LOG_TRANSFER
VerifyOrReturn(!gBDXDiagnosticLogsProvider.IsBusy(), AddResponse(commandObj, path, StatusEnum::kBusy));
auto err = gBDXDiagnosticLogsProvider.InitializeTransfer(commandObj, path, delegate, intent, transferFileDesignator.Value());
VerifyOrReturn(CHIP_NO_ERROR == err, AddResponse(commandObj, path, StatusEnum::kDenied));
#else
HandleLogRequestForResponsePayload(commandObj, path, intent, StatusEnum::kExhausted);
#endif // CHIP_CONFIG_ENABLE_BDX_LOG_TRANSFER
}
} // namespace DiagnosticLogs
} // namespace Clusters
} // namespace app
} // namespace chip
bool emberAfDiagnosticLogsClusterRetrieveLogsRequestCallback(chip::app::CommandHandler * commandObj,
const chip::app::ConcreteCommandPath & commandPath,
const Commands::RetrieveLogsRequest::DecodableType & commandData)
{
// If the Intent and/or the RequestedProtocol arguments contain invalid (out of range) values the command SHALL fail with a
// Status Code of INVALID_COMMAND.
auto intent = commandData.intent;
auto protocol = commandData.requestedProtocol;
if (intent == IntentEnum::kUnknownEnumValue || protocol == TransferProtocolEnum::kUnknownEnumValue)
{
commandObj->AddStatus(commandPath, Status::InvalidCommand);
return true;
}
auto instance = DiagnosticLogsServer::Instance();
if (protocol == TransferProtocolEnum::kResponsePayload)
{
instance.HandleLogRequestForResponsePayload(commandObj, commandPath, intent);
}
else
{
instance.HandleLogRequestForBdx(commandObj, commandPath, intent, commandData.transferFileDesignator);
}
return true;
}
void MatterDiagnosticLogsPluginServerInitCallback() {}
#endif // #ifdef MATTER_DM_DIAGNOSTIC_LOGS_CLUSTER_SERVER_ENDPOINT_COUNT