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 = std::make_shared<std::optional<std::vector<uint8_t>>>(); 40 do 41 { 42 if (tai.status() != ActionStatus::success) 43 { 44 fprintf(stderr, 45 "LogBlobHandler: Log file unit failed for %s\n", 46 infoP->blobId.c_str()); 47 continue; 48 } 49 if (!infoP->handler->open("", std::ios::in)) 50 { 51 fprintf(stderr, 52 "LogBlobHandler: Opening log file failed for %s\n", 53 infoP->blobId.c_str()); 54 continue; 55 } 56 auto d = infoP->handler->read( 57 0, std::numeric_limits<uint32_t>::max()); 58 infoP->handler->close(); 59 if (!d) 60 { 61 fprintf(stderr, 62 "LogBlobHandler: Reading log file failed for %s\n", 63 infoP->blobId.c_str()); 64 continue; 65 } 66 *data = std::move(d); 67 } while (false); 68 for (auto sessionP : infoP->sessionsToUpdate) 69 { 70 sessionP->data = data; 71 } 72 infoP->sessionsToUpdate.clear(); 73 }); 74 if (!blobInfoMap.try_emplace(info->blobId, std::move(info)).second) 75 { 76 fprintf(stderr, 77 "LogBlobHandler: Ignoring duplicate config for %s\n", 78 info->blobId.c_str()); 79 } 80 } 81 } 82 83 bool LogBlobHandler::canHandleBlob(const std::string& path) 84 { 85 return blobInfoMap.find(path) != blobInfoMap.end(); 86 } 87 88 std::vector<std::string> LogBlobHandler::getBlobIds() 89 { 90 std::vector<std::string> ret; 91 for (const auto& [key, _] : blobInfoMap) 92 { 93 ret.emplace_back(key); 94 } 95 return ret; 96 } 97 98 /** 99 * deleteBlob - does nothing, always fails 100 */ 101 bool LogBlobHandler::deleteBlob(const std::string& path) 102 { 103 for (const auto& [sessionId, sessionInfo] : sessionInfoMap) 104 { 105 if (sessionInfo->blob->blobId == path) 106 { 107 fprintf(stderr, 108 "LogBlobHandler: delete %s fail: there is an open session " 109 "for this blob\n", 110 path.c_str()); 111 return false; 112 } 113 } 114 115 auto* blob = blobInfoMap.at(path).get(); 116 if (!blob->actions->onDelete->trigger()) 117 { 118 fprintf(stderr, 119 "LogBlobHandler: delete %s fail: onDelete trigger failed\n", 120 path.c_str()); 121 return false; 122 } 123 return true; 124 } 125 126 bool LogBlobHandler::stat(const std::string&, blobs::BlobMeta*) 127 { 128 return false; 129 } 130 131 bool LogBlobHandler::open(uint16_t session, uint16_t flags, 132 const std::string& path) 133 { 134 /* only reads are supported, check if blob is handled and make sure 135 * the blob isn't already opened 136 */ 137 if (flags != blobs::read) 138 { 139 fprintf(stderr, 140 "LogBlobHandler: open %s fail: unsupported flags(0x%04X.)\n", 141 path.c_str(), flags); 142 return false; 143 } 144 145 auto info = std::make_unique<SessionInfo>(); 146 info->blob = blobInfoMap.at(path).get(); 147 info->blob->sessionsToUpdate.emplace(info.get()); 148 if (info->blob->sessionsToUpdate.size() == 1 && 149 !info->blob->actions->onOpen->trigger()) 150 { 151 fprintf(stderr, "LogBlobHandler: open %s fail: onOpen trigger failed\n", 152 path.c_str()); 153 info->blob->sessionsToUpdate.erase(info.get()); 154 return false; 155 } 156 157 sessionInfoMap[session] = std::move(info); 158 return true; 159 } 160 161 std::vector<uint8_t> LogBlobHandler::read(uint16_t session, uint32_t offset, 162 uint32_t requestedSize) 163 { 164 auto& data = sessionInfoMap.at(session)->data; 165 if (data == nullptr || !*data) 166 { 167 throw std::runtime_error("LogBlobHandler: Log data not ready for read"); 168 } 169 if ((*data)->size() < offset) 170 { 171 return {}; 172 } 173 std::vector<uint8_t> ret( 174 std::min<size_t>(requestedSize, (*data)->size() - offset)); 175 std::memcpy(&ret[0], &(**data)[offset], ret.size()); 176 return ret; 177 } 178 179 bool LogBlobHandler::close(uint16_t session) 180 { 181 auto it = sessionInfoMap.find(session); 182 if (it == sessionInfoMap.end()) 183 { 184 return false; 185 } 186 auto& info = *it->second; 187 info.blob->sessionsToUpdate.erase(&info); 188 if (info.blob->sessionsToUpdate.empty()) 189 { 190 info.blob->actions->onOpen->abort(); 191 } 192 sessionInfoMap.erase(it); 193 return true; 194 } 195 196 bool LogBlobHandler::stat(uint16_t session, blobs::BlobMeta* meta) 197 { 198 const auto& data = sessionInfoMap.at(session)->data; 199 if (data == nullptr) 200 { 201 meta->blobState = blobs::StateFlags::committing; 202 meta->size = 0; 203 } 204 else if (!*data) 205 { 206 meta->blobState = blobs::StateFlags::commit_error; 207 meta->size = 0; 208 } 209 else 210 { 211 meta->blobState = blobs::StateFlags::committed | 212 blobs::StateFlags::open_read; 213 meta->size = (*data)->size(); 214 } 215 return true; 216 } 217 218 bool LogBlobHandler::expire(uint16_t session) 219 { 220 close(session); 221 return true; 222 } 223 224 } // namespace ipmi_flash 225