xref: /openbmc/phosphor-ipmi-blobs/manager.cpp (revision fea1d8115bace4bedae2523a9d9389a990adb09f)
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