1 /* 2 * Copyright 2018 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "manager.hpp" 18 19 #include <algorithm> 20 #include <iostream> 21 #include <memory> 22 #include <string> 23 #include <vector> 24 25 namespace blobs 26 { 27 28 bool BlobManager::registerHandler(std::unique_ptr<GenericBlobInterface> handler) 29 { 30 if (!handler) 31 { 32 return false; 33 } 34 35 handlers.push_back(std::move(handler)); 36 return true; 37 } 38 39 uint32_t BlobManager::buildBlobList() 40 { 41 /* Clear out the current list (IPMI handler is presently single-threaded). 42 */ 43 ids.clear(); 44 45 /* Grab the list of blobs and extend the local list */ 46 for (const auto& h : handlers) 47 { 48 std::vector<std::string> blobs = h->getBlobIds(); 49 ids.insert(ids.end(), blobs.begin(), blobs.end()); 50 } 51 52 return ids.size(); 53 } 54 55 std::string BlobManager::getBlobId(uint32_t index) 56 { 57 /* Range check. */ 58 if (index >= ids.size()) 59 { 60 return ""; 61 } 62 63 return ids[index]; 64 } 65 66 bool BlobManager::open(uint16_t flags, const std::string& path, 67 uint16_t* session) 68 { 69 GenericBlobInterface* handler = getHandler(path); 70 71 /* No handler found. */ 72 if (!handler) 73 { 74 return false; 75 } 76 77 /* No sessions available... */ 78 if (!getSession(session)) 79 { 80 return false; 81 } 82 83 /* Verify flags - must be at least read or write */ 84 if (!(flags & (OpenFlags::read | OpenFlags::write))) 85 { 86 /* Neither read not write set, which means calls to Read/Write will 87 * reject. */ 88 return false; 89 } 90 91 /* Try to clean up anything that's falling out of cleanup timeout for this 92 * handler */ 93 cleanUpStaleSessions(handler); 94 95 if (!handler->open(*session, flags, path)) 96 { 97 return false; 98 } 99 100 /* Associate session with handler */ 101 sessions[*session] = SessionInfo(path, handler, flags); 102 openSessions[handler].insert(*session); 103 openFiles[path]++; 104 return true; 105 } 106 107 bool BlobManager::stat(const std::string& path, BlobMeta* meta) 108 { 109 /* meta should never be NULL. */ 110 GenericBlobInterface* handler = getHandler(path); 111 112 /* No handler found. */ 113 if (!handler) 114 { 115 return false; 116 } 117 118 return handler->stat(path, meta); 119 } 120 121 bool BlobManager::stat(uint16_t session, BlobMeta* meta) 122 { 123 if (auto handler = getActionHandler(session)) 124 { 125 return handler->stat(session, meta); 126 } 127 return false; 128 } 129 130 bool BlobManager::commit(uint16_t session, const std::vector<uint8_t>& data) 131 { 132 if (auto handler = getActionHandler(session)) 133 { 134 return handler->commit(session, data); 135 } 136 return false; 137 } 138 139 bool BlobManager::close(uint16_t session) 140 { 141 if (auto handler = getActionHandler(session)) 142 { 143 if (!handler->close(session)) 144 { 145 return false; 146 } 147 eraseSession(handler, session); 148 return true; 149 } 150 return false; 151 } 152 153 std::vector<uint8_t> BlobManager::read(uint16_t session, uint32_t offset, 154 uint32_t requestedSize) 155 { 156 /* TODO: Currently, configure_ac isn't finding libuserlayer, w.r.t the 157 * symbols I need. 158 */ 159 160 /** The channel to use for now. 161 * TODO: We will receive this information through the IPMI message call. 162 */ 163 // const int ipmiChannel = ipmi::currentChNum; 164 /** This information is transport specific. 165 * TODO: We need a way to know this dynamically. 166 * on BT, 4 bytes of header, and 1 reply code. 167 */ 168 // uint32_t maxTransportSize = ipmi::getChannelMaxTransferSize(ipmiChannel); 169 170 if (auto handler = getActionHandler(session, OpenFlags::read)) 171 { 172 return handler->read(session, offset, 173 std::min(maximumReadSize, requestedSize)); 174 } 175 return {}; 176 } 177 178 bool BlobManager::write(uint16_t session, uint32_t offset, 179 const std::vector<uint8_t>& data) 180 { 181 if (auto handler = getActionHandler(session, OpenFlags::write)) 182 { 183 return handler->write(session, offset, data); 184 } 185 return false; 186 } 187 188 bool BlobManager::deleteBlob(const std::string& path) 189 { 190 GenericBlobInterface* handler = getHandler(path); 191 192 /* No handler found. */ 193 if (!handler) 194 { 195 return false; 196 } 197 198 /* Check if the file has any open handles. */ 199 if (openFiles[path] > 0) 200 { 201 return false; 202 } 203 204 /* Try deleting it. */ 205 return handler->deleteBlob(path); 206 } 207 208 bool BlobManager::writeMeta(uint16_t session, uint32_t offset, 209 const std::vector<uint8_t>& data) 210 { 211 if (auto handler = getActionHandler(session)) 212 { 213 return handler->writeMeta(session, offset, data); 214 } 215 return false; 216 } 217 218 bool BlobManager::getSession(uint16_t* sess) 219 { 220 uint16_t tries = 0; 221 222 if (!sess) 223 { 224 return false; 225 } 226 227 /* This is not meant to fail as you have 64KiB values available. */ 228 229 /* TODO(venture): We could just count the keys in the session map to know 230 * if it's full. 231 */ 232 do 233 { 234 uint16_t lsess = next++; 235 if (!sessions.count(lsess)) 236 { 237 /* value not in use, return it. */ 238 (*sess) = lsess; 239 return true; 240 } 241 } while (++tries < 0xffff); 242 243 return false; 244 } 245 246 GenericBlobInterface* BlobManager::getHandler(const std::string& path) 247 { 248 /* Find a handler. */ 249 auto h = std::find_if( 250 handlers.begin(), handlers.end(), 251 [&path](const auto& iter) { return (iter->canHandleBlob(path)); }); 252 if (h != handlers.end()) 253 { 254 return h->get(); 255 } 256 257 return nullptr; 258 } 259 260 GenericBlobInterface* BlobManager::getActionHandler(uint16_t session, 261 uint16_t requiredFlags) 262 { 263 if (auto item = sessions.find(session); 264 item != sessions.end() && (item->second.flags & requiredFlags)) 265 { 266 item->second.lastActionTime = std::chrono::steady_clock::now(); 267 return item->second.handler; 268 } 269 return nullptr; 270 } 271 272 void BlobManager::eraseSession(GenericBlobInterface* const handler, 273 uint16_t session) 274 { 275 if (auto item = sessions.find(session); item != sessions.end()) 276 { 277 const auto& blobId = item->second.blobId; 278 279 /* Ok for openSessions[handler] to be an empty set */ 280 openSessions[handler].erase(session); 281 openFiles[blobId]--; 282 if (openFiles[blobId] == 0) 283 { 284 openFiles.erase(blobId); 285 } 286 287 /* Erase at the end after using the session info */ 288 sessions.erase(session); 289 } 290 } 291 292 void BlobManager::cleanUpStaleSessions(GenericBlobInterface* const handler) 293 { 294 if (openSessions.count(handler) == 0) 295 { 296 return; 297 } 298 299 auto timeNow = std::chrono::steady_clock::now(); 300 std::set<uint16_t> expiredSet; 301 302 for (auto sessionId : openSessions[handler]) 303 { 304 if (timeNow - sessions[sessionId].lastActionTime >= sessionTimeout) 305 { 306 expiredSet.insert(sessionId); 307 } 308 } 309 310 for (auto sessionId : expiredSet) 311 { 312 std::cerr << "phosphor-ipmi-blobs: expiring stale session " << sessionId 313 << std::endl; 314 315 /* We do a best case recovery by issuing an expire call. If it fails 316 * don't erase sessions since the handler side might be still tracking 317 * it as open. */ 318 if (handler->expire(sessionId)) 319 { 320 eraseSession(handler, sessionId); 321 } 322 else 323 { 324 std::cerr << "phosphor-ipmi-blobs: failed to expire session " 325 << sessionId << std::endl; 326 } 327 } 328 } 329 330 static std::unique_ptr<BlobManager> manager; 331 332 ManagerInterface* getBlobManager() 333 { 334 if (manager == nullptr) 335 { 336 manager = std::make_unique<BlobManager>(); 337 } 338 339 return manager.get(); 340 } 341 342 } // namespace blobs 343