| #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 |