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