xref: /openbmc/phosphor-ipmi-blobs/manager.cpp (revision e0c41de4cc07a3e72046c658a9b8da9410f9a8fa)
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  
registerHandler(std::unique_ptr<GenericBlobInterface> handler)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  
buildBlobList()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  
getBlobId(uint32_t index)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  
open(uint16_t flags,const std::string & path,uint16_t * session)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  
stat(const std::string & path,BlobMeta * meta)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  
stat(uint16_t session,BlobMeta * meta)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  
commit(uint16_t session,const std::vector<uint8_t> & data)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  
close(uint16_t session)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  
read(uint16_t session,uint32_t offset,uint32_t requestedSize)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  
write(uint16_t session,uint32_t offset,const std::vector<uint8_t> & data)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  
deleteBlob(const std::string & path)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  
writeMeta(uint16_t session,uint32_t offset,const std::vector<uint8_t> & data)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  
getSession(uint16_t * sess)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  
getHandler(const std::string & path)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  
getActionHandler(uint16_t session,uint16_t requiredFlags)259  GenericBlobInterface* BlobManager::getActionHandler(uint16_t session,
260                                                      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  
eraseSession(GenericBlobInterface * const handler,uint16_t session)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  
cleanUpStaleSessions(GenericBlobInterface * const handler)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  
getBlobManager()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