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