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