blob: 15ff083475bd81e8f5de097673c379dbf5cdddb7 [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
Kun Yib61a88b2019-11-12 22:50:34 -080028void 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);
Kun Yi9ce83482019-11-14 13:08:39 -080033
34 auto path = getPath(session);
35 openFiles[path]--;
36 if (openFiles[path] == 0)
37 {
38 openFiles.erase(path);
39 }
Kun Yib61a88b2019-11-12 22:50:34 -080040}
41
42void 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
Patrick Ventureef3aead2018-09-12 08:53:29 -070080bool 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
91uint32_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 */
Patrick Venturea6e21a02018-10-23 09:45:04 -070098 for (const auto& h : handlers)
Patrick Ventureef3aead2018-09-12 08:53:29 -070099 {
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
107std::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
118bool 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
Patrick Venture2f581512019-01-10 09:30:36 -0800129 /* No sessions available... */
Patrick Ventureef3aead2018-09-12 08:53:29 -0700130 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
Kun Yib61a88b2019-11-12 22:50:34 -0800143 /* Try to clean up anything that's falling out of cleanup timeout for this
144 * handler */
145 cleanUpStaleSessions(handler);
146
Patrick Ventureef3aead2018-09-12 08:53:29 -0700147 if (!handler->open(*session, flags, path))
148 {
149 return false;
150 }
151
152 /* Associate session with handler */
153 sessions[*session] = SessionInfo(path, handler, flags);
Kun Yib61a88b2019-11-12 22:50:34 -0800154 openSessions[handler].insert(*session);
Kun Yi9ce83482019-11-14 13:08:39 -0800155 openFiles[path]++;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700156 return true;
157}
158
159GenericBlobInterface* BlobManager::getHandler(const std::string& path)
160{
161 /* Find a handler. */
Patrick Venturee50d4e42018-10-23 09:50:27 -0700162 auto h = std::find_if(
163 handlers.begin(), handlers.end(),
164 [&path](const auto& iter) { return (iter->canHandleBlob(path)); });
165 if (h != handlers.end())
Patrick Ventureef3aead2018-09-12 08:53:29 -0700166 {
Patrick Venturee50d4e42018-10-23 09:50:27 -0700167 return h->get();
Patrick Ventureef3aead2018-09-12 08:53:29 -0700168 }
169
Patrick Venturee50d4e42018-10-23 09:50:27 -0700170 return nullptr;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700171}
172
Patrick Ventureef3aead2018-09-12 08:53:29 -0700173std::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
Patrick Venture8bc11772019-06-04 07:20:24 -0700184bool BlobManager::stat(const std::string& path, BlobMeta* meta)
Patrick Ventureef3aead2018-09-12 08:53:29 -0700185{
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
Patrick Venture8bc11772019-06-04 07:20:24 -0700198bool BlobManager::stat(uint16_t session, BlobMeta* meta)
Patrick Ventureef3aead2018-09-12 08:53:29 -0700199{
Kun Yic892f4a2019-11-19 13:42:46 -0800200 if (auto handler = getActionHandle(session))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700201 {
Kun Yic892f4a2019-11-19 13:42:46 -0800202 return handler->stat(session, meta);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700203 }
Kun Yic892f4a2019-11-19 13:42:46 -0800204 return false;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700205}
206
207bool BlobManager::commit(uint16_t session, const std::vector<uint8_t>& data)
208{
Kun Yic892f4a2019-11-19 13:42:46 -0800209 if (auto handler = getActionHandle(session))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700210 {
Kun Yic892f4a2019-11-19 13:42:46 -0800211 return handler->commit(session, data);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700212 }
Kun Yic892f4a2019-11-19 13:42:46 -0800213 return false;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700214}
215
216bool BlobManager::close(uint16_t session)
217{
Kun Yic892f4a2019-11-19 13:42:46 -0800218 if (auto handler = getActionHandle(session))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700219 {
Kun Yic892f4a2019-11-19 13:42:46 -0800220 if (!handler->close(session))
221 {
222 return false;
223 }
Kun Yib61a88b2019-11-12 22:50:34 -0800224 eraseSession(handler, session);
Kun Yic892f4a2019-11-19 13:42:46 -0800225 return true;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700226 }
Kun Yic892f4a2019-11-19 13:42:46 -0800227 return false;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700228}
229
230std::vector<uint8_t> BlobManager::read(uint16_t session, uint32_t offset,
231 uint32_t requestedSize)
232{
Patrick Venture86c87f52019-02-01 14:44:15 -0800233 /* 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
Kun Yic892f4a2019-11-19 13:42:46 -0800247 if (auto handler = getActionHandle(session, OpenFlags::read))
248 {
249 return handler->read(session, offset,
250 std::min(maximumReadSize, requestedSize));
251 }
252 return {};
Patrick Ventureef3aead2018-09-12 08:53:29 -0700253}
254
255bool BlobManager::write(uint16_t session, uint32_t offset,
256 const std::vector<uint8_t>& data)
257{
Kun Yic892f4a2019-11-19 13:42:46 -0800258 if (auto handler = getActionHandle(session, OpenFlags::write))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700259 {
Kun Yic892f4a2019-11-19 13:42:46 -0800260 return handler->write(session, offset, data);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700261 }
Kun Yic892f4a2019-11-19 13:42:46 -0800262 return {};
Patrick Ventureef3aead2018-09-12 08:53:29 -0700263}
264
265bool 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. */
Kun Yi9ce83482019-11-14 13:08:39 -0800276 if (openFiles[path] > 0)
Patrick Ventureef3aead2018-09-12 08:53:29 -0700277 {
278 return false;
279 }
280
281 /* Try deleting it. */
282 return handler->deleteBlob(path);
283}
284
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700285bool BlobManager::writeMeta(uint16_t session, uint32_t offset,
286 const std::vector<uint8_t>& data)
287{
Kun Yic892f4a2019-11-19 13:42:46 -0800288 if (auto handler = getActionHandle(session))
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700289 {
Kun Yic892f4a2019-11-19 13:42:46 -0800290 return handler->writeMeta(session, offset, data);
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700291 }
Kun Yic892f4a2019-11-19 13:42:46 -0800292 return false;
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700293}
294
Patrick Ventureef3aead2018-09-12 08:53:29 -0700295bool BlobManager::getSession(uint16_t* sess)
296{
297 uint16_t tries = 0;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700298
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 {
Patrick Venturec9ad5ff2018-10-12 17:05:49 -0700311 uint16_t lsess = next++;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700312 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}
Patrick Ventureb3e07e22018-09-27 15:11:57 -0700322
323static std::unique_ptr<BlobManager> manager;
324
Patrick Venture73eb6872018-10-01 18:37:34 -0700325ManagerInterface* getBlobManager()
Patrick Ventureb3e07e22018-09-27 15:11:57 -0700326{
327 if (manager == nullptr)
328 {
329 manager = std::make_unique<BlobManager>();
330 }
331
332 return manager.get();
333}
334
Patrick Ventureef3aead2018-09-12 08:53:29 -0700335} // namespace blobs