1 // Copyright 2021 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "log_handler.hpp" 16 17 #include <algorithm> 18 #include <cstring> 19 #include <ios> 20 #include <limits> 21 #include <memory> 22 #include <optional> 23 #include <utility> 24 #include <vector> 25 26 namespace ipmi_flash 27 { 28 29 LogBlobHandler::LogBlobHandler(std::vector<HandlerConfig<ActionPack>>&& configs) 30 { 31 for (auto& config : configs) 32 { 33 auto info = std::make_unique<BlobInfo>(); 34 info->blobId = std::move(config.blobId); 35 info->actions = std::move(config.actions); 36 info->handler = std::move(config.handler); 37 info->actions->onOpen->setCallback( 38 [infoP = info.get()](TriggerableActionInterface& tai) { 39 auto data = 40 std::make_shared<std::optional<std::vector<uint8_t>>>(); 41 do 42 { 43 if (tai.status() != ActionStatus::success) 44 { 45 fprintf(stderr, 46 "LogBlobHandler: Log file unit failed for %s\n", 47 infoP->blobId.c_str()); 48 continue; 49 } 50 if (!infoP->handler->open("", std::ios::in)) 51 { 52 fprintf( 53 stderr, 54 "LogBlobHandler: Opening log file failed for %s\n", 55 infoP->blobId.c_str()); 56 continue; 57 } 58 auto d = infoP->handler->read( 59 0, std::numeric_limits<uint32_t>::max()); 60 infoP->handler->close(); 61 if (!d) 62 { 63 fprintf( 64 stderr, 65 "LogBlobHandler: Reading log file failed for %s\n", 66 infoP->blobId.c_str()); 67 continue; 68 } 69 *data = std::move(d); 70 } while (false); 71 for (auto sessionP : infoP->sessionsToUpdate) 72 { 73 sessionP->data = data; 74 } 75 infoP->sessionsToUpdate.clear(); 76 }); 77 if (!blobInfoMap.try_emplace(info->blobId, std::move(info)).second) 78 { 79 fprintf(stderr, 80 "LogBlobHandler: Ignoring duplicate config for %s\n", 81 info->blobId.c_str()); 82 } 83 } 84 } 85 86 bool LogBlobHandler::canHandleBlob(const std::string& path) 87 { 88 return blobInfoMap.find(path) != blobInfoMap.end(); 89 } 90 91 std::vector<std::string> LogBlobHandler::getBlobIds() 92 { 93 std::vector<std::string> ret; 94 for (const auto& [key, _] : blobInfoMap) 95 { 96 ret.emplace_back(key); 97 } 98 return ret; 99 } 100 101 /** 102 * deleteBlob - does nothing, always fails 103 */ 104 bool LogBlobHandler::deleteBlob(const std::string& path) 105 { 106 for (const auto& [sessionId, sessionInfo] : sessionInfoMap) 107 { 108 if (sessionInfo->blob->blobId == path) 109 { 110 fprintf(stderr, 111 "LogBlobHandler: delete %s fail: there is an open session " 112 "for this blob\n", 113 path.c_str()); 114 return false; 115 } 116 } 117 118 auto* blob = blobInfoMap.at(path).get(); 119 if (!blob->actions->onDelete->trigger()) 120 { 121 fprintf(stderr, 122 "LogBlobHandler: delete %s fail: onDelete trigger failed\n", 123 path.c_str()); 124 return false; 125 } 126 return true; 127 } 128 129 bool LogBlobHandler::stat(const std::string&, blobs::BlobMeta*) 130 { 131 return false; 132 } 133 134 bool LogBlobHandler::open(uint16_t session, uint16_t flags, 135 const std::string& path) 136 { 137 /* only reads are supported, check if blob is handled and make sure 138 * the blob isn't already opened 139 */ 140 if (flags != blobs::read) 141 { 142 fprintf(stderr, 143 "LogBlobHandler: open %s fail: unsupported flags(0x%04X.)\n", 144 path.c_str(), flags); 145 return false; 146 } 147 148 auto info = std::make_unique<SessionInfo>(); 149 info->blob = blobInfoMap.at(path).get(); 150 info->blob->sessionsToUpdate.emplace(info.get()); 151 if (info->blob->sessionsToUpdate.size() == 1 && 152 !info->blob->actions->onOpen->trigger()) 153 { 154 fprintf(stderr, "LogBlobHandler: open %s fail: onOpen trigger failed\n", 155 path.c_str()); 156 info->blob->sessionsToUpdate.erase(info.get()); 157 return false; 158 } 159 160 sessionInfoMap[session] = std::move(info); 161 return true; 162 } 163 164 std::vector<uint8_t> LogBlobHandler::read(uint16_t session, uint32_t offset, 165 uint32_t requestedSize) 166 { 167 auto& data = sessionInfoMap.at(session)->data; 168 if (data == nullptr || !*data) 169 { 170 throw std::runtime_error("LogBlobHandler: Log data not ready for read"); 171 } 172 if ((*data)->size() < offset) 173 { 174 return {}; 175 } 176 std::vector<uint8_t> ret( 177 std::min<size_t>(requestedSize, (*data)->size() - offset)); 178 std::memcpy(&ret[0], &(**data)[offset], ret.size()); 179 return ret; 180 } 181 182 bool LogBlobHandler::close(uint16_t session) 183 { 184 auto it = sessionInfoMap.find(session); 185 if (it == sessionInfoMap.end()) 186 { 187 return false; 188 } 189 auto& info = *it->second; 190 info.blob->sessionsToUpdate.erase(&info); 191 if (info.blob->sessionsToUpdate.empty()) 192 { 193 info.blob->actions->onOpen->abort(); 194 } 195 sessionInfoMap.erase(it); 196 return true; 197 } 198 199 bool LogBlobHandler::stat(uint16_t session, blobs::BlobMeta* meta) 200 { 201 const auto& data = sessionInfoMap.at(session)->data; 202 if (data == nullptr) 203 { 204 meta->blobState = blobs::StateFlags::committing; 205 meta->size = 0; 206 } 207 else if (!*data) 208 { 209 meta->blobState = blobs::StateFlags::commit_error; 210 meta->size = 0; 211 } 212 else 213 { 214 meta->blobState = blobs::StateFlags::committed | 215 blobs::StateFlags::open_read; 216 meta->size = (*data)->size(); 217 } 218 return true; 219 } 220 221 bool LogBlobHandler::expire(uint16_t session) 222 { 223 close(session); 224 return true; 225 } 226 227 } // namespace ipmi_flash 228