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, requestedSize); 173 } 174 return {}; 175 } 176 177 bool BlobManager::write(uint16_t session, uint32_t offset, 178 const std::vector<uint8_t>& data) 179 { 180 if (auto handler = getActionHandler(session, OpenFlags::write)) 181 { 182 return handler->write(session, offset, data); 183 } 184 return false; 185 } 186 187 bool BlobManager::deleteBlob(const std::string& path) 188 { 189 GenericBlobInterface* handler = getHandler(path); 190 191 /* No handler found. */ 192 if (!handler) 193 { 194 return false; 195 } 196 197 /* Check if the file has any open handles. */ 198 if (openFiles[path] > 0) 199 { 200 return false; 201 } 202 203 /* Try deleting it. */ 204 return handler->deleteBlob(path); 205 } 206 207 bool BlobManager::writeMeta(uint16_t session, uint32_t offset, 208 const std::vector<uint8_t>& data) 209 { 210 if (auto handler = getActionHandler(session)) 211 { 212 return handler->writeMeta(session, offset, data); 213 } 214 return false; 215 } 216 217 bool BlobManager::getSession(uint16_t* sess) 218 { 219 uint16_t tries = 0; 220 221 if (!sess) 222 { 223 return false; 224 } 225 226 /* This is not meant to fail as you have 64KiB values available. */ 227 228 /* TODO(venture): We could just count the keys in the session map to know 229 * if it's full. 230 */ 231 do 232 { 233 uint16_t lsess = next++; 234 if (!sessions.count(lsess)) 235 { 236 /* value not in use, return it. */ 237 (*sess) = lsess; 238 return true; 239 } 240 } while (++tries < 0xffff); 241 242 return false; 243 } 244 245 GenericBlobInterface* BlobManager::getHandler(const std::string& path) 246 { 247 /* Find a handler. */ 248 auto h = std::find_if( 249 handlers.begin(), handlers.end(), 250 [&path](const auto& iter) { return (iter->canHandleBlob(path)); }); 251 if (h != handlers.end()) 252 { 253 return h->get(); 254 } 255 256 return nullptr; 257 } 258 259 GenericBlobInterface* 260 BlobManager::getActionHandler(uint16_t session, uint16_t requiredFlags) 261 { 262 if (auto item = sessions.find(session); 263 item != sessions.end() && (item->second.flags & requiredFlags)) 264 { 265 item->second.lastActionTime = std::chrono::steady_clock::now(); 266 return item->second.handler; 267 } 268 return nullptr; 269 } 270 271 void BlobManager::eraseSession(GenericBlobInterface* const handler, 272 uint16_t session) 273 { 274 if (auto item = sessions.find(session); item != sessions.end()) 275 { 276 const auto& blobId = item->second.blobId; 277 278 /* Ok for openSessions[handler] to be an empty set */ 279 openSessions[handler].erase(session); 280 openFiles[blobId]--; 281 if (openFiles[blobId] == 0) 282 { 283 openFiles.erase(blobId); 284 } 285 286 /* Erase at the end after using the session info */ 287 sessions.erase(session); 288 } 289 } 290 291 void BlobManager::cleanUpStaleSessions(GenericBlobInterface* const handler) 292 { 293 if (openSessions.count(handler) == 0) 294 { 295 return; 296 } 297 298 auto timeNow = std::chrono::steady_clock::now(); 299 std::set<uint16_t> expiredSet; 300 301 for (auto sessionId : openSessions[handler]) 302 { 303 if (timeNow - sessions[sessionId].lastActionTime >= sessionTimeout) 304 { 305 expiredSet.insert(sessionId); 306 } 307 } 308 309 for (auto sessionId : expiredSet) 310 { 311 std::cerr << "phosphor-ipmi-blobs: expiring stale session " << sessionId 312 << std::endl; 313 314 /* We do a best case recovery by issuing an expire call. If it fails 315 * don't erase sessions since the handler side might be still tracking 316 * it as open. */ 317 if (handler->expire(sessionId)) 318 { 319 eraseSession(handler, sessionId); 320 } 321 else 322 { 323 std::cerr << "phosphor-ipmi-blobs: failed to expire session " 324 << sessionId << std::endl; 325 } 326 } 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