xref: /openbmc/phosphor-ipmi-blobs/manager.cpp (revision 9ce834822ed625512913e14b83c50487d4b2c12b)
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 std::string BlobManager::getPath(uint16_t session) const
174 {
175     auto item = sessions.find(session);
176     if (item == sessions.end())
177     {
178         return "";
179     }
180 
181     return item->second.blobId;
182 }
183 
184 bool BlobManager::stat(const std::string& path, BlobMeta* meta)
185 {
186     /* meta should never be NULL. */
187     GenericBlobInterface* handler = getHandler(path);
188 
189     /* No handler found. */
190     if (!handler)
191     {
192         return false;
193     }
194 
195     return handler->stat(path, meta);
196 }
197 
198 bool BlobManager::stat(uint16_t session, BlobMeta* meta)
199 {
200     if (auto handler = getActionHandle(session))
201     {
202         return handler->stat(session, meta);
203     }
204     return false;
205 }
206 
207 bool BlobManager::commit(uint16_t session, const std::vector<uint8_t>& data)
208 {
209     if (auto handler = getActionHandle(session))
210     {
211         return handler->commit(session, data);
212     }
213     return false;
214 }
215 
216 bool BlobManager::close(uint16_t session)
217 {
218     if (auto handler = getActionHandle(session))
219     {
220         if (!handler->close(session))
221         {
222             return false;
223         }
224         eraseSession(handler, session);
225         return true;
226     }
227     return false;
228 }
229 
230 std::vector<uint8_t> BlobManager::read(uint16_t session, uint32_t offset,
231                                        uint32_t requestedSize)
232 {
233     /* TODO: Currently, configure_ac isn't finding libuserlayer, w.r.t the
234      * symbols I need.
235      */
236 
237     /** The channel to use for now.
238      * TODO: We will receive this information through the IPMI message call.
239      */
240     // const int ipmiChannel = ipmi::currentChNum;
241     /** This information is transport specific.
242      * TODO: We need a way to know this dynamically.
243      * on BT, 4 bytes of header, and 1 reply code.
244      */
245     // uint32_t maxTransportSize = ipmi::getChannelMaxTransferSize(ipmiChannel);
246 
247     if (auto handler = getActionHandle(session, OpenFlags::read))
248     {
249         return handler->read(session, offset,
250                              std::min(maximumReadSize, requestedSize));
251     }
252     return {};
253 }
254 
255 bool BlobManager::write(uint16_t session, uint32_t offset,
256                         const std::vector<uint8_t>& data)
257 {
258     if (auto handler = getActionHandle(session, OpenFlags::write))
259     {
260         return handler->write(session, offset, data);
261     }
262     return {};
263 }
264 
265 bool BlobManager::deleteBlob(const std::string& path)
266 {
267     GenericBlobInterface* handler = getHandler(path);
268 
269     /* No handler found. */
270     if (!handler)
271     {
272         return false;
273     }
274 
275     /* Check if the file has any open handles. */
276     if (openFiles[path] > 0)
277     {
278         return false;
279     }
280 
281     /* Try deleting it. */
282     return handler->deleteBlob(path);
283 }
284 
285 bool BlobManager::writeMeta(uint16_t session, uint32_t offset,
286                             const std::vector<uint8_t>& data)
287 {
288     if (auto handler = getActionHandle(session))
289     {
290         return handler->writeMeta(session, offset, data);
291     }
292     return false;
293 }
294 
295 bool BlobManager::getSession(uint16_t* sess)
296 {
297     uint16_t tries = 0;
298 
299     if (!sess)
300     {
301         return false;
302     }
303 
304     /* This is not meant to fail as you have 64KiB values available. */
305 
306     /* TODO(venture): We could just count the keys in the session map to know
307      * if it's full.
308      */
309     do
310     {
311         uint16_t lsess = next++;
312         if (!sessions.count(lsess))
313         {
314             /* value not in use, return it. */
315             (*sess) = lsess;
316             return true;
317         }
318     } while (++tries < 0xffff);
319 
320     return false;
321 }
322 
323 static std::unique_ptr<BlobManager> manager;
324 
325 ManagerInterface* getBlobManager()
326 {
327     if (manager == nullptr)
328     {
329         manager = std::make_unique<BlobManager>();
330     }
331 
332     return manager.get();
333 }
334 
335 } // namespace blobs
336