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