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