/* * * 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 "include/diagnostic-logs-provider-delegate-impl.h" #include #include using namespace chip; using namespace chip::app::Clusters::DiagnosticLogs; LogProvider LogProvider::sInstance; namespace { bool IsValidIntent(IntentEnum intent) { return intent != IntentEnum::kUnknownEnumValue; } size_t GetFileSize(FILE * fp) { VerifyOrReturnValue(nullptr != fp, 0); auto offset = ftell(fp); VerifyOrReturnValue(offset != -1, 0); auto rv = fseek(fp, 0, SEEK_END); VerifyOrReturnValue(rv == 0, 0); auto fileSize = ftell(fp); VerifyOrReturnValue(fileSize != -1, 0); rv = fseek(fp, offset, SEEK_SET); VerifyOrReturnValue(rv == 0, 0); return static_cast(fileSize); } } // namespace LogProvider::~LogProvider() { for (auto f : mFiles) { auto rv = fclose(f.second); if (rv != 0) { ChipLogError(NotSpecified, "Error when closing file pointer: %p (%d)", f.second, errno); } } mFiles.clear(); } CHIP_ERROR LogProvider::GetLogForIntent(IntentEnum intent, MutableByteSpan & outBuffer, Optional & outTimeStamp, Optional & outTimeSinceBoot) { CHIP_ERROR err = CHIP_NO_ERROR; LogSessionHandle sessionHandle = kInvalidLogSessionHandle; err = StartLogCollection(intent, sessionHandle, outTimeStamp, outTimeSinceBoot); VerifyOrReturnError(CHIP_NO_ERROR == err, err, outBuffer.reduce_size(0)); bool unusedOutIsEndOfLog; err = CollectLog(sessionHandle, outBuffer, unusedOutIsEndOfLog); VerifyOrReturnError(CHIP_NO_ERROR == err, err, outBuffer.reduce_size(0)); err = EndLogCollection(sessionHandle); VerifyOrReturnError(CHIP_NO_ERROR == err, err, outBuffer.reduce_size(0)); return CHIP_NO_ERROR; } CHIP_ERROR LogProvider::StartLogCollection(IntentEnum intent, LogSessionHandle & outHandle, Optional & outTimeStamp, Optional & outTimeSinceBoot) { VerifyOrReturnValue(IsValidIntent(intent), CHIP_ERROR_INVALID_ARGUMENT); auto filePath = GetFilePathForIntent(intent); VerifyOrReturnValue(filePath.HasValue(), CHIP_ERROR_NOT_FOUND); auto fp = fopen(filePath.Value().c_str(), "rb"); VerifyOrReturnValue(!(nullptr == fp && errno == ENOENT), CHIP_ERROR_NOT_FOUND); VerifyOrReturnValue(nullptr != fp, CHIP_ERROR_INTERNAL); mLogSessionHandle++; // If the session handle rolls over to UINT16_MAX which is invalid, reset to 0. VerifyOrDo(mLogSessionHandle != kInvalidLogSessionHandle, mLogSessionHandle = 0); outHandle = mLogSessionHandle; mFiles[mLogSessionHandle] = fp; return CHIP_NO_ERROR; } CHIP_ERROR LogProvider::EndLogCollection(LogSessionHandle sessionHandle) { VerifyOrReturnValue(sessionHandle != kInvalidLogSessionHandle, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnValue(mFiles.count(sessionHandle), CHIP_ERROR_INVALID_ARGUMENT); auto fp = mFiles[sessionHandle]; mFiles.erase(sessionHandle); auto rv = fclose(fp); VerifyOrReturnError(rv == 0, CHIP_ERROR_POSIX(errno)); return CHIP_NO_ERROR; } CHIP_ERROR LogProvider::CollectLog(LogSessionHandle sessionHandle, MutableByteSpan & outBuffer, bool & outIsEndOfLog) { VerifyOrReturnValue(sessionHandle != kInvalidLogSessionHandle, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnValue(mFiles.count(sessionHandle), CHIP_ERROR_INVALID_ARGUMENT); auto fp = mFiles[sessionHandle]; auto fileSize = GetFileSize(fp); auto count = std::min(fileSize, outBuffer.size()); VerifyOrReturnError(CanCastTo(count), CHIP_ERROR_INVALID_ARGUMENT, outBuffer.reduce_size(0)); auto bytesRead = fread(outBuffer.data(), 1, count, fp); VerifyOrReturnError(CanCastTo(bytesRead), CHIP_ERROR_INTERNAL, outBuffer.reduce_size(0)); outBuffer.reduce_size(static_cast(bytesRead)); outIsEndOfLog = fileSize == static_cast(ftell(fp)); return CHIP_NO_ERROR; } size_t LogProvider::GetSizeForIntent(IntentEnum intent) { VerifyOrReturnValue(IsValidIntent(intent), 0); auto filePath = GetFilePathForIntent(intent); VerifyOrReturnValue(filePath.HasValue(), 0); auto fp = fopen(filePath.Value().c_str(), "rb"); VerifyOrReturnValue(nullptr != fp, 0); auto fileSize = GetFileSize(fp); auto rv = fclose(fp); if (rv != 0) { ChipLogError(NotSpecified, "Error when closing file pointer (%d)", errno); } return fileSize; } Optional LogProvider::GetFilePathForIntent(IntentEnum intent) const { switch (intent) { case IntentEnum::kEndUserSupport: return mEndUserSupportLogFilePath; case IntentEnum::kNetworkDiag: return mNetworkDiagnosticsLogFilePath; case IntentEnum::kCrashLogs: return mCrashLogFilePath; case IntentEnum::kUnknownEnumValue: // It should never happen. chipDie(); } return NullOptional; }