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