blob: 566354c0f48ed91dc1ec70c89953e2bd2d27ad4a [file] [log] [blame] [edit]
#include "biossettings_callback.h"
#include <fstream>
#include <sstream>
#include <string>
#include <filesystem> // NOLINT
#include <system_error> // NOLINT
#include "absl/log/log.h"
#include "grpcblob_defs.h"
#include "grpcpp/server_context.h"
#include "grpcpp/support/status.h"
#include "google/protobuf/text_format.h"
namespace blobs {
// Read and Write the simple message should be quick enough, we can use sync
// implementation.
grpc::Status BiosSettingsServiceImpl::GetBiosSettings(
[[maybe_unused]] grpc::ServerContext* context,
[[maybe_unused]] const google::protobuf::Empty* request,
phosphor::bios::BiosSettings* reply) {
std::string filename{biosSettingsConfigFile};
if ((instance + 1) != numInstances) {
// Not last instance, assume we are multi-host system, append
// "_instance" suffix
filename += "_" + std::to_string(instance);
}
std::ifstream ifs(filename);
std::string errorMessage;
if (!ifs.is_open()) {
errorMessage = "Failed to open " + filename;
LOG(ERROR) << errorMessage;
return {grpc::StatusCode::NOT_FOUND, errorMessage};
}
std::stringstream buffer;
buffer << ifs.rdbuf();
std::string content = buffer.str();
if (content.empty()) {
errorMessage = "Empty " + filename;
LOG(ERROR) << errorMessage;
return {grpc::StatusCode::INTERNAL, errorMessage};
}
google::protobuf::TextFormat::Parser parser;
if (!parser.ParseFromString(content, reply)) {
errorMessage = "Failed to parse " + filename;
LOG(ERROR) << errorMessage;
return {grpc::StatusCode::INTERNAL, errorMessage};
}
LOG(INFO) << "Received GetBiosSettings gRPC";
return grpc::Status::OK;
}
grpc::Status BiosSettingsServiceImpl::SetBiosSettings(
[[maybe_unused]] grpc::ServerContext* context,
const phosphor::bios::BiosSettings* request,
[[maybe_unused]] google::protobuf::Empty* reply) {
std::string filename{biosSettingsConfigFile};
if ((instance + 1) != numInstances) {
// Not last instance, assume we are multi-host system, append
// "_instance" suffix
filename += "_" + std::to_string(instance);
}
phosphor::bios::BiosSettings settings;
settings.CopyFrom(*request);
std::string errorMessage;
std::string text;
google::protobuf::TextFormat::Printer printer;
if (!printer.PrintToString(settings, &text)) {
errorMessage = "Failed to transform BiosSettings message into text format";
LOG(ERROR) << errorMessage;
return {grpc::StatusCode::INVALID_ARGUMENT, errorMessage};
}
std::ofstream out(filename);
if (!out) {
errorMessage = "Failed to open " + filename;
LOG(ERROR) << errorMessage;
return {grpc::StatusCode::INTERNAL, errorMessage};
}
out << text;
out.close();
if (!out) {
errorMessage = "Failed to write " + filename;
LOG(ERROR) << errorMessage;
return {grpc::StatusCode::INTERNAL, errorMessage};
}
LOG(INFO) << "Received SetBiosSettingsStatus gRPC";
return grpc::Status::OK;
}
grpc::Status BiosSettingsServiceImpl::GetSettings(
[[maybe_unused]] grpc::ServerContext* context,
[[maybe_unused]] const google::protobuf::Empty* request,
phosphor::bios::OpaqueSettings* reply) {
std::string filename{configuredFilepath};
if ((instance + 1) != numInstances) {
// Not last instance, assume we are multi-host system, append
// "_instance" suffix
filename += "_" + std::to_string(instance);
}
std::ifstream ifs(filename);
std::string errorMessage;
if (!ifs.is_open()) {
errorMessage = "Failed to open " + filename;
LOG(ERROR) << errorMessage;
return {grpc::StatusCode::NOT_FOUND, errorMessage};
}
std::stringstream buffer;
buffer << ifs.rdbuf();
std::string content = buffer.str();
if (content.empty()) {
errorMessage = "Empty " + filename;
LOG(ERROR) << errorMessage;
return {grpc::StatusCode::INTERNAL, errorMessage};
}
reply->set_content(content);
LOG(INFO) << "GetSettings: " << content;
return grpc::Status::OK;
}
grpc::Status BiosSettingsServiceImpl::ReportSettings(
[[maybe_unused]] grpc::ServerContext* context,
const phosphor::bios::OpaqueSettings* request,
[[maybe_unused]] google::protobuf::Empty* reply) {
std::string filename{reportedFilepath};
if ((instance + 1) != numInstances) {
// Not last instance, assume we are multi-host system, append
// "_instance" suffix
filename += "_" + std::to_string(instance);
}
// Skip write operation if content doesn't change
std::ifstream ifs(filename);
if (ifs.is_open()) {
std::stringstream buffer;
buffer << ifs.rdbuf();
std::string content = buffer.str();
if (content == request->content()) {
LOG(INFO) << "ReportSettings: " << request->content();
return grpc::Status::OK;
}
}
ifs.close();
// Create parent directories if it doesn't exist
std::filesystem::path pathName{filename};
std::filesystem::path pathDir = pathName.parent_path();
std::error_code err =
std::make_error_code(std::errc::no_such_file_or_directory);
std::string errorMessage;
std::filesystem::create_directories(pathDir, err);
if (err) {
errorMessage = "Failed to create directory: " + pathDir.string() +
" due to " + err.message();
LOG(ERROR) << errorMessage;
return {grpc::StatusCode::INTERNAL, errorMessage};
}
// Write to temporary file
std::string tempFilename{filename + ".tmp"};
std::ofstream out(tempFilename);
if (!out) {
errorMessage = "Failed to open " + tempFilename;
LOG(ERROR) << errorMessage;
return {grpc::StatusCode::INTERNAL, errorMessage};
}
out << request->content();
out.close();
if (!out) {
errorMessage = "Failed to write " + tempFilename;
LOG(ERROR) << errorMessage;
return {grpc::StatusCode::INTERNAL, errorMessage};
}
// Rename ensures other processes see either the old file or the new file, but
// not an inconsistent, partially written state.
std::filesystem::rename(tempFilename, filename, err);
if (err) {
errorMessage =
"Failed to rename " + tempFilename + "due to " + err.message();
LOG(ERROR) << errorMessage;
return {grpc::StatusCode::INTERNAL, errorMessage};
}
LOG(INFO) << "ReportSettings: " << request->content();
return grpc::Status::OK;
}
} // namespace blobs