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 std::string BlobManager::getPath(uint16_t session) const 174 { 175 auto item = sessions.find(session); 176 if (item == sessions.end()) 177 { 178 return ""; 179 } 180 181 return item->second.blobId; 182 } 183 184 bool BlobManager::stat(const std::string& path, BlobMeta* meta) 185 { 186 /* meta should never be NULL. */ 187 GenericBlobInterface* handler = getHandler(path); 188 189 /* No handler found. */ 190 if (!handler) 191 { 192 return false; 193 } 194 195 return handler->stat(path, meta); 196 } 197 198 bool BlobManager::stat(uint16_t session, BlobMeta* meta) 199 { 200 if (auto handler = getActionHandle(session)) 201 { 202 return handler->stat(session, meta); 203 } 204 return false; 205 } 206 207 bool BlobManager::commit(uint16_t session, const std::vector<uint8_t>& data) 208 { 209 if (auto handler = getActionHandle(session)) 210 { 211 return handler->commit(session, data); 212 } 213 return false; 214 } 215 216 bool BlobManager::close(uint16_t session) 217 { 218 if (auto handler = getActionHandle(session)) 219 { 220 if (!handler->close(session)) 221 { 222 return false; 223 } 224 eraseSession(handler, session); 225 return true; 226 } 227 return false; 228 } 229 230 std::vector<uint8_t> BlobManager::read(uint16_t session, uint32_t offset, 231 uint32_t requestedSize) 232 { 233 /* TODO: Currently, configure_ac isn't finding libuserlayer, w.r.t the 234 * symbols I need. 235 */ 236 237 /** The channel to use for now. 238 * TODO: We will receive this information through the IPMI message call. 239 */ 240 // const int ipmiChannel = ipmi::currentChNum; 241 /** This information is transport specific. 242 * TODO: We need a way to know this dynamically. 243 * on BT, 4 bytes of header, and 1 reply code. 244 */ 245 // uint32_t maxTransportSize = ipmi::getChannelMaxTransferSize(ipmiChannel); 246 247 if (auto handler = getActionHandle(session, OpenFlags::read)) 248 { 249 return handler->read(session, offset, 250 std::min(maximumReadSize, requestedSize)); 251 } 252 return {}; 253 } 254 255 bool BlobManager::write(uint16_t session, uint32_t offset, 256 const std::vector<uint8_t>& data) 257 { 258 if (auto handler = getActionHandle(session, OpenFlags::write)) 259 { 260 return handler->write(session, offset, data); 261 } 262 return {}; 263 } 264 265 bool BlobManager::deleteBlob(const std::string& path) 266 { 267 GenericBlobInterface* handler = getHandler(path); 268 269 /* No handler found. */ 270 if (!handler) 271 { 272 return false; 273 } 274 275 /* Check if the file has any open handles. */ 276 if (openFiles[path] > 0) 277 { 278 return false; 279 } 280 281 /* Try deleting it. */ 282 return handler->deleteBlob(path); 283 } 284 285 bool BlobManager::writeMeta(uint16_t session, uint32_t offset, 286 const std::vector<uint8_t>& data) 287 { 288 if (auto handler = getActionHandle(session)) 289 { 290 return handler->writeMeta(session, offset, data); 291 } 292 return false; 293 } 294 295 bool BlobManager::getSession(uint16_t* sess) 296 { 297 uint16_t tries = 0; 298 299 if (!sess) 300 { 301 return false; 302 } 303 304 /* This is not meant to fail as you have 64KiB values available. */ 305 306 /* TODO(venture): We could just count the keys in the session map to know 307 * if it's full. 308 */ 309 do 310 { 311 uint16_t lsess = next++; 312 if (!sessions.count(lsess)) 313 { 314 /* value not in use, return it. */ 315 (*sess) = lsess; 316 return true; 317 } 318 } while (++tries < 0xffff); 319 320 return false; 321 } 322 323 static std::unique_ptr<BlobManager> manager; 324 325 ManagerInterface* getBlobManager() 326 { 327 if (manager == nullptr) 328 { 329 manager = std::make_unique<BlobManager>(); 330 } 331 332 return manager.get(); 333 } 334 335 } // namespace blobs 336