/* * Copyright 2018 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "manager.hpp" #include #include #include #include #include namespace blobs { void BlobManager::incrementOpen(const std::string& path) { if (path.empty()) { return; } openFiles[path] += 1; } void BlobManager::decrementOpen(const std::string& path) { if (path.empty()) { return; } /* TODO(venture): Check into the iterator from find, does it makes sense * to just update it directly? */ auto entry = openFiles.find(path); if (entry != openFiles.end()) { /* found it, decrement it and remove it if 0. */ openFiles[path] -= 1; if (openFiles[path] == 0) { openFiles.erase(path); } } } int BlobManager::getOpen(const std::string& path) const { /* No need to input check on the read-only call. */ auto entry = openFiles.find(path); if (entry != openFiles.end()) { return entry->second; } return 0; } void BlobManager::eraseSession(GenericBlobInterface* handler, uint16_t session) { sessions.erase(session); /* Ok for openSessions[handler] to be an empty set */ openSessions[handler].erase(session); decrementOpen(getPath(session)); } void BlobManager::cleanUpStaleSessions(GenericBlobInterface* handler) { if (openSessions.count(handler) == 0) { return; } auto timeNow = std::chrono::steady_clock::now(); std::set expiredSet; for (auto sessionId : openSessions[handler]) { if (timeNow - sessions[sessionId].lastActionTime >= sessionTimeout) { expiredSet.insert(sessionId); } } for (auto sessionId : expiredSet) { std::cerr << "phosphor-ipmi-blobs: expiring stale session " << sessionId << std::endl; /* We do a best case recovery by issuing an expire call. If it fails * don't erase sessions since the handler side might be still tracking * it as open. */ if (handler->expire(sessionId)) { eraseSession(handler, sessionId); } else { std::cerr << "phosphor-ipmi-blobs: failed to expire session " << sessionId << std::endl; } } } bool BlobManager::registerHandler(std::unique_ptr handler) { if (!handler) { return false; } handlers.push_back(std::move(handler)); return true; } uint32_t BlobManager::buildBlobList() { /* Clear out the current list (IPMI handler is presently single-threaded). */ ids.clear(); /* Grab the list of blobs and extend the local list */ for (const auto& h : handlers) { std::vector blobs = h->getBlobIds(); ids.insert(ids.end(), blobs.begin(), blobs.end()); } return ids.size(); } std::string BlobManager::getBlobId(uint32_t index) { /* Range check. */ if (index >= ids.size()) { return ""; } return ids[index]; } bool BlobManager::open(uint16_t flags, const std::string& path, uint16_t* session) { GenericBlobInterface* handler = getHandler(path); /* No handler found. */ if (!handler) { return false; } /* No sessions available... */ if (!getSession(session)) { return false; } /* Verify flags - must be at least read or write */ if (!(flags & (OpenFlags::read | OpenFlags::write))) { /* Neither read not write set, which means calls to Read/Write will * reject. */ return false; } /* Try to clean up anything that's falling out of cleanup timeout for this * handler */ cleanUpStaleSessions(handler); if (!handler->open(*session, flags, path)) { return false; } /* Associate session with handler */ sessions[*session] = SessionInfo(path, handler, flags); openSessions[handler].insert(*session); incrementOpen(path); return true; } GenericBlobInterface* BlobManager::getHandler(const std::string& path) { /* Find a handler. */ auto h = std::find_if( handlers.begin(), handlers.end(), [&path](const auto& iter) { return (iter->canHandleBlob(path)); }); if (h != handlers.end()) { return h->get(); } return nullptr; } std::string BlobManager::getPath(uint16_t session) const { auto item = sessions.find(session); if (item == sessions.end()) { return ""; } return item->second.blobId; } bool BlobManager::stat(const std::string& path, BlobMeta* meta) { /* meta should never be NULL. */ GenericBlobInterface* handler = getHandler(path); /* No handler found. */ if (!handler) { return false; } return handler->stat(path, meta); } bool BlobManager::stat(uint16_t session, BlobMeta* meta) { if (auto handler = getActionHandle(session)) { return handler->stat(session, meta); } return false; } bool BlobManager::commit(uint16_t session, const std::vector& data) { if (auto handler = getActionHandle(session)) { return handler->commit(session, data); } return false; } bool BlobManager::close(uint16_t session) { if (auto handler = getActionHandle(session)) { if (!handler->close(session)) { return false; } eraseSession(handler, session); return true; } return false; } std::vector BlobManager::read(uint16_t session, uint32_t offset, uint32_t requestedSize) { /* TODO: Currently, configure_ac isn't finding libuserlayer, w.r.t the * symbols I need. */ /** The channel to use for now. * TODO: We will receive this information through the IPMI message call. */ // const int ipmiChannel = ipmi::currentChNum; /** This information is transport specific. * TODO: We need a way to know this dynamically. * on BT, 4 bytes of header, and 1 reply code. */ // uint32_t maxTransportSize = ipmi::getChannelMaxTransferSize(ipmiChannel); if (auto handler = getActionHandle(session, OpenFlags::read)) { return handler->read(session, offset, std::min(maximumReadSize, requestedSize)); } return {}; } bool BlobManager::write(uint16_t session, uint32_t offset, const std::vector& data) { if (auto handler = getActionHandle(session, OpenFlags::write)) { return handler->write(session, offset, data); } return {}; } bool BlobManager::deleteBlob(const std::string& path) { GenericBlobInterface* handler = getHandler(path); /* No handler found. */ if (!handler) { return false; } /* Check if the file has any open handles. */ if (getOpen(path) > 0) { return false; } /* Try deleting it. */ return handler->deleteBlob(path); } bool BlobManager::writeMeta(uint16_t session, uint32_t offset, const std::vector& data) { if (auto handler = getActionHandle(session)) { return handler->writeMeta(session, offset, data); } return false; } bool BlobManager::getSession(uint16_t* sess) { uint16_t tries = 0; if (!sess) { return false; } /* This is not meant to fail as you have 64KiB values available. */ /* TODO(venture): We could just count the keys in the session map to know * if it's full. */ do { uint16_t lsess = next++; if (!sessions.count(lsess)) { /* value not in use, return it. */ (*sess) = lsess; return true; } } while (++tries < 0xffff); return false; } static std::unique_ptr manager; ManagerInterface* getBlobManager() { if (manager == nullptr) { manager = std::make_unique(); } return manager.get(); } } // namespace blobs