xref: /openbmc/phosphor-ipmi-blobs/manager.cpp (revision beee9e54ae3e20bce5e466773cfda9232382e2e7)
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     if (auto item = sessions.find(session); item != sessions.end())
31     {
32         const auto& blobId = item->second.blobId;
33 
34         /* Ok for openSessions[handler] to be an empty set */
35         openSessions[handler].erase(session);
36         openFiles[blobId]--;
37         if (openFiles[blobId] == 0)
38         {
39             openFiles.erase(blobId);
40         }
41 
42         /* Erase at the end after using the session info */
43         sessions.erase(session);
44     }
45 }
46 
47 void BlobManager::cleanUpStaleSessions(GenericBlobInterface* handler)
48 {
49     if (openSessions.count(handler) == 0)
50     {
51         return;
52     }
53 
54     auto timeNow = std::chrono::steady_clock::now();
55     std::set<uint16_t> expiredSet;
56 
57     for (auto sessionId : openSessions[handler])
58     {
59         if (timeNow - sessions[sessionId].lastActionTime >= sessionTimeout)
60         {
61             expiredSet.insert(sessionId);
62         }
63     }
64 
65     for (auto sessionId : expiredSet)
66     {
67         std::cerr << "phosphor-ipmi-blobs: expiring stale session " << sessionId
68                   << std::endl;
69 
70         /* We do a best case recovery by issuing an expire call. If it fails
71          * don't erase sessions since the handler side might be still tracking
72          * it as open. */
73         if (handler->expire(sessionId))
74         {
75             eraseSession(handler, sessionId);
76         }
77         else
78         {
79             std::cerr << "phosphor-ipmi-blobs: failed to expire session "
80                       << sessionId << std::endl;
81         }
82     }
83 }
84 
85 bool BlobManager::registerHandler(std::unique_ptr<GenericBlobInterface> handler)
86 {
87     if (!handler)
88     {
89         return false;
90     }
91 
92     handlers.push_back(std::move(handler));
93     return true;
94 }
95 
96 uint32_t BlobManager::buildBlobList()
97 {
98     /* Clear out the current list (IPMI handler is presently single-threaded).
99      */
100     ids.clear();
101 
102     /* Grab the list of blobs and extend the local list */
103     for (const auto& h : handlers)
104     {
105         std::vector<std::string> blobs = h->getBlobIds();
106         ids.insert(ids.end(), blobs.begin(), blobs.end());
107     }
108 
109     return ids.size();
110 }
111 
112 std::string BlobManager::getBlobId(uint32_t index)
113 {
114     /* Range check. */
115     if (index >= ids.size())
116     {
117         return "";
118     }
119 
120     return ids[index];
121 }
122 
123 bool BlobManager::open(uint16_t flags, const std::string& path,
124                        uint16_t* session)
125 {
126     GenericBlobInterface* handler = getHandler(path);
127 
128     /* No handler found. */
129     if (!handler)
130     {
131         return false;
132     }
133 
134     /* No sessions available... */
135     if (!getSession(session))
136     {
137         return false;
138     }
139 
140     /* Verify flags - must be at least read or write */
141     if (!(flags & (OpenFlags::read | OpenFlags::write)))
142     {
143         /* Neither read not write set, which means calls to Read/Write will
144          * reject. */
145         return false;
146     }
147 
148     /* Try to clean up anything that's falling out of cleanup timeout for this
149      * handler */
150     cleanUpStaleSessions(handler);
151 
152     if (!handler->open(*session, flags, path))
153     {
154         return false;
155     }
156 
157     /* Associate session with handler */
158     sessions[*session] = SessionInfo(path, handler, flags);
159     openSessions[handler].insert(*session);
160     openFiles[path]++;
161     return true;
162 }
163 
164 GenericBlobInterface* BlobManager::getHandler(const std::string& path)
165 {
166     /* Find a handler. */
167     auto h = std::find_if(
168         handlers.begin(), handlers.end(),
169         [&path](const auto& iter) { return (iter->canHandleBlob(path)); });
170     if (h != handlers.end())
171     {
172         return h->get();
173     }
174 
175     return nullptr;
176 }
177 
178 GenericBlobInterface* BlobManager::getActionHandle(uint16_t session,
179                                                    uint16_t requiredFlags)
180 {
181     if (auto item = sessions.find(session);
182         item != sessions.end() && (item->second.flags & requiredFlags))
183     {
184         item->second.lastActionTime = std::chrono::steady_clock::now();
185         return item->second.handler;
186     }
187     return nullptr;
188 }
189 
190 bool BlobManager::stat(const std::string& path, BlobMeta* meta)
191 {
192     /* meta should never be NULL. */
193     GenericBlobInterface* handler = getHandler(path);
194 
195     /* No handler found. */
196     if (!handler)
197     {
198         return false;
199     }
200 
201     return handler->stat(path, meta);
202 }
203 
204 bool BlobManager::stat(uint16_t session, BlobMeta* meta)
205 {
206     if (auto handler = getActionHandle(session))
207     {
208         return handler->stat(session, meta);
209     }
210     return false;
211 }
212 
213 bool BlobManager::commit(uint16_t session, const std::vector<uint8_t>& data)
214 {
215     if (auto handler = getActionHandle(session))
216     {
217         return handler->commit(session, data);
218     }
219     return false;
220 }
221 
222 bool BlobManager::close(uint16_t session)
223 {
224     if (auto handler = getActionHandle(session))
225     {
226         if (!handler->close(session))
227         {
228             return false;
229         }
230         eraseSession(handler, session);
231         return true;
232     }
233     return false;
234 }
235 
236 std::vector<uint8_t> BlobManager::read(uint16_t session, uint32_t offset,
237                                        uint32_t requestedSize)
238 {
239     /* TODO: Currently, configure_ac isn't finding libuserlayer, w.r.t the
240      * symbols I need.
241      */
242 
243     /** The channel to use for now.
244      * TODO: We will receive this information through the IPMI message call.
245      */
246     // const int ipmiChannel = ipmi::currentChNum;
247     /** This information is transport specific.
248      * TODO: We need a way to know this dynamically.
249      * on BT, 4 bytes of header, and 1 reply code.
250      */
251     // uint32_t maxTransportSize = ipmi::getChannelMaxTransferSize(ipmiChannel);
252 
253     if (auto handler = getActionHandle(session, OpenFlags::read))
254     {
255         return handler->read(session, offset,
256                              std::min(maximumReadSize, requestedSize));
257     }
258     return {};
259 }
260 
261 bool BlobManager::write(uint16_t session, uint32_t offset,
262                         const std::vector<uint8_t>& data)
263 {
264     if (auto handler = getActionHandle(session, OpenFlags::write))
265     {
266         return handler->write(session, offset, data);
267     }
268     return false;
269 }
270 
271 bool BlobManager::deleteBlob(const std::string& path)
272 {
273     GenericBlobInterface* handler = getHandler(path);
274 
275     /* No handler found. */
276     if (!handler)
277     {
278         return false;
279     }
280 
281     /* Check if the file has any open handles. */
282     if (openFiles[path] > 0)
283     {
284         return false;
285     }
286 
287     /* Try deleting it. */
288     return handler->deleteBlob(path);
289 }
290 
291 bool BlobManager::writeMeta(uint16_t session, uint32_t offset,
292                             const std::vector<uint8_t>& data)
293 {
294     if (auto handler = getActionHandle(session))
295     {
296         return handler->writeMeta(session, offset, data);
297     }
298     return false;
299 }
300 
301 bool BlobManager::getSession(uint16_t* sess)
302 {
303     uint16_t tries = 0;
304 
305     if (!sess)
306     {
307         return false;
308     }
309 
310     /* This is not meant to fail as you have 64KiB values available. */
311 
312     /* TODO(venture): We could just count the keys in the session map to know
313      * if it's full.
314      */
315     do
316     {
317         uint16_t lsess = next++;
318         if (!sessions.count(lsess))
319         {
320             /* value not in use, return it. */
321             (*sess) = lsess;
322             return true;
323         }
324     } while (++tries < 0xffff);
325 
326     return false;
327 }
328 
329 static std::unique_ptr<BlobManager> manager;
330 
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