blob: ba019d4d7e73bdd10f26a858675bb32ff7f975c7 [file] [log] [blame]
Patrick Ventureef3aead2018-09-12 08:53:29 -07001/*
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
Patrick Venturecd8dab42019-01-15 19:57:38 -080017#include "manager.hpp"
18
Patrick Venturee50d4e42018-10-23 09:50:27 -070019#include <algorithm>
Kun Yib61a88b2019-11-12 22:50:34 -080020#include <iostream>
Patrick Ventureb3e07e22018-09-27 15:11:57 -070021#include <memory>
Patrick Ventureef3aead2018-09-12 08:53:29 -070022#include <string>
23#include <vector>
24
25namespace blobs
26{
27
Patrick Ventureef3aead2018-09-12 08:53:29 -070028bool 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
39uint32_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 */
Patrick Venturea6e21a02018-10-23 09:45:04 -070046 for (const auto& h : handlers)
Patrick Ventureef3aead2018-09-12 08:53:29 -070047 {
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
55std::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
66bool 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
Patrick Venture2f581512019-01-10 09:30:36 -080077 /* No sessions available... */
Patrick Ventureef3aead2018-09-12 08:53:29 -070078 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
Kun Yib61a88b2019-11-12 22:50:34 -080091 /* Try to clean up anything that's falling out of cleanup timeout for this
92 * handler */
93 cleanUpStaleSessions(handler);
94
Patrick Ventureef3aead2018-09-12 08:53:29 -070095 if (!handler->open(*session, flags, path))
96 {
97 return false;
98 }
99
100 /* Associate session with handler */
101 sessions[*session] = SessionInfo(path, handler, flags);
Kun Yib61a88b2019-11-12 22:50:34 -0800102 openSessions[handler].insert(*session);
Kun Yi9ce83482019-11-14 13:08:39 -0800103 openFiles[path]++;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700104 return true;
105}
106
Patrick Venture8bc11772019-06-04 07:20:24 -0700107bool BlobManager::stat(const std::string& path, BlobMeta* meta)
Patrick Ventureef3aead2018-09-12 08:53:29 -0700108{
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
Patrick Venture8bc11772019-06-04 07:20:24 -0700121bool BlobManager::stat(uint16_t session, BlobMeta* meta)
Patrick Ventureef3aead2018-09-12 08:53:29 -0700122{
Kun Yi9cd8f762019-11-22 20:11:42 -0800123 if (auto handler = getActionHandler(session))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700124 {
Kun Yic892f4a2019-11-19 13:42:46 -0800125 return handler->stat(session, meta);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700126 }
Kun Yic892f4a2019-11-19 13:42:46 -0800127 return false;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700128}
129
130bool BlobManager::commit(uint16_t session, const std::vector<uint8_t>& data)
131{
Kun Yi9cd8f762019-11-22 20:11:42 -0800132 if (auto handler = getActionHandler(session))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700133 {
Kun Yic892f4a2019-11-19 13:42:46 -0800134 return handler->commit(session, data);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700135 }
Kun Yic892f4a2019-11-19 13:42:46 -0800136 return false;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700137}
138
139bool BlobManager::close(uint16_t session)
140{
Kun Yi9cd8f762019-11-22 20:11:42 -0800141 if (auto handler = getActionHandler(session))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700142 {
Kun Yic892f4a2019-11-19 13:42:46 -0800143 if (!handler->close(session))
144 {
145 return false;
146 }
Kun Yib61a88b2019-11-12 22:50:34 -0800147 eraseSession(handler, session);
Kun Yic892f4a2019-11-19 13:42:46 -0800148 return true;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700149 }
Kun Yic892f4a2019-11-19 13:42:46 -0800150 return false;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700151}
152
153std::vector<uint8_t> BlobManager::read(uint16_t session, uint32_t offset,
154 uint32_t requestedSize)
155{
Patrick Venture86c87f52019-02-01 14:44:15 -0800156 /* 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
Kun Yi9cd8f762019-11-22 20:11:42 -0800170 if (auto handler = getActionHandler(session, OpenFlags::read))
Kun Yic892f4a2019-11-19 13:42:46 -0800171 {
Willy Tu83f99922022-06-22 14:59:07 -0700172 return handler->read(session, offset, requestedSize);
Kun Yic892f4a2019-11-19 13:42:46 -0800173 }
174 return {};
Patrick Ventureef3aead2018-09-12 08:53:29 -0700175}
176
177bool BlobManager::write(uint16_t session, uint32_t offset,
178 const std::vector<uint8_t>& data)
179{
Kun Yi9cd8f762019-11-22 20:11:42 -0800180 if (auto handler = getActionHandler(session, OpenFlags::write))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700181 {
Kun Yic892f4a2019-11-19 13:42:46 -0800182 return handler->write(session, offset, data);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700183 }
Kun Yifea1d812019-11-14 11:38:44 -0800184 return false;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700185}
186
187bool 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. */
Kun Yi9ce83482019-11-14 13:08:39 -0800198 if (openFiles[path] > 0)
Patrick Ventureef3aead2018-09-12 08:53:29 -0700199 {
200 return false;
201 }
202
203 /* Try deleting it. */
204 return handler->deleteBlob(path);
205}
206
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700207bool BlobManager::writeMeta(uint16_t session, uint32_t offset,
208 const std::vector<uint8_t>& data)
209{
Kun Yi9cd8f762019-11-22 20:11:42 -0800210 if (auto handler = getActionHandler(session))
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700211 {
Kun Yic892f4a2019-11-19 13:42:46 -0800212 return handler->writeMeta(session, offset, data);
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700213 }
Kun Yic892f4a2019-11-19 13:42:46 -0800214 return false;
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700215}
216
Patrick Ventureef3aead2018-09-12 08:53:29 -0700217bool BlobManager::getSession(uint16_t* sess)
218{
219 uint16_t tries = 0;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700220
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 {
Patrick Venturec9ad5ff2018-10-12 17:05:49 -0700233 uint16_t lsess = next++;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700234 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}
Patrick Ventureb3e07e22018-09-27 15:11:57 -0700244
Kun Yi9cd8f762019-11-22 20:11:42 -0800245GenericBlobInterface* 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
259GenericBlobInterface* BlobManager::getActionHandler(uint16_t session,
260 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
271void 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
291void 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
Patrick Ventureb3e07e22018-09-27 15:11:57 -0700329static std::unique_ptr<BlobManager> manager;
330
Patrick Venture73eb6872018-10-01 18:37:34 -0700331ManagerInterface* getBlobManager()
Patrick Ventureb3e07e22018-09-27 15:11:57 -0700332{
333 if (manager == nullptr)
334 {
335 manager = std::make_unique<BlobManager>();
336 }
337
338 return manager.get();
339}
340
Patrick Ventureef3aead2018-09-12 08:53:29 -0700341} // namespace blobs