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