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