blob: 870bf60fbee15136399c7b51673c52951277a8f7 [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{
Kun Yib61a88b2019-11-12 22:50:34 -080030 /* Ok for openSessions[handler] to be an empty set */
31 openSessions[handler].erase(session);
Kun Yi9ce83482019-11-14 13:08:39 -080032
33 auto path = getPath(session);
34 openFiles[path]--;
35 if (openFiles[path] == 0)
36 {
37 openFiles.erase(path);
38 }
Kun Yicd833aa2019-11-22 14:59:40 -080039 /* Cannot erase before getPath() is called */
40 sessions.erase(session);
Kun Yib61a88b2019-11-12 22:50:34 -080041}
42
43void 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
Patrick Ventureef3aead2018-09-12 08:53:29 -070081bool 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
92uint32_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 */
Patrick Venturea6e21a02018-10-23 09:45:04 -070099 for (const auto& h : handlers)
Patrick Ventureef3aead2018-09-12 08:53:29 -0700100 {
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
108std::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
119bool 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
Patrick Venture2f581512019-01-10 09:30:36 -0800130 /* No sessions available... */
Patrick Ventureef3aead2018-09-12 08:53:29 -0700131 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
Kun Yib61a88b2019-11-12 22:50:34 -0800144 /* Try to clean up anything that's falling out of cleanup timeout for this
145 * handler */
146 cleanUpStaleSessions(handler);
147
Patrick Ventureef3aead2018-09-12 08:53:29 -0700148 if (!handler->open(*session, flags, path))
149 {
150 return false;
151 }
152
153 /* Associate session with handler */
154 sessions[*session] = SessionInfo(path, handler, flags);
Kun Yib61a88b2019-11-12 22:50:34 -0800155 openSessions[handler].insert(*session);
Kun Yi9ce83482019-11-14 13:08:39 -0800156 openFiles[path]++;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700157 return true;
158}
159
160GenericBlobInterface* BlobManager::getHandler(const std::string& path)
161{
162 /* Find a handler. */
Patrick Venturee50d4e42018-10-23 09:50:27 -0700163 auto h = std::find_if(
164 handlers.begin(), handlers.end(),
165 [&path](const auto& iter) { return (iter->canHandleBlob(path)); });
166 if (h != handlers.end())
Patrick Ventureef3aead2018-09-12 08:53:29 -0700167 {
Patrick Venturee50d4e42018-10-23 09:50:27 -0700168 return h->get();
Patrick Ventureef3aead2018-09-12 08:53:29 -0700169 }
170
Patrick Venturee50d4e42018-10-23 09:50:27 -0700171 return nullptr;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700172}
173
Kun Yifea1d812019-11-14 11:38:44 -0800174GenericBlobInterface* 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
Patrick Ventureef3aead2018-09-12 08:53:29 -0700186std::string BlobManager::getPath(uint16_t session) const
187{
Kun Yifea1d812019-11-14 11:38:44 -0800188 if (auto item = sessions.find(session); item != sessions.end())
Patrick Ventureef3aead2018-09-12 08:53:29 -0700189 {
Kun Yifea1d812019-11-14 11:38:44 -0800190 return item->second.blobId;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700191 }
192
Kun Yifea1d812019-11-14 11:38:44 -0800193 return "";
Patrick Ventureef3aead2018-09-12 08:53:29 -0700194}
195
Patrick Venture8bc11772019-06-04 07:20:24 -0700196bool BlobManager::stat(const std::string& path, BlobMeta* meta)
Patrick Ventureef3aead2018-09-12 08:53:29 -0700197{
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
Patrick Venture8bc11772019-06-04 07:20:24 -0700210bool BlobManager::stat(uint16_t session, BlobMeta* meta)
Patrick Ventureef3aead2018-09-12 08:53:29 -0700211{
Kun Yic892f4a2019-11-19 13:42:46 -0800212 if (auto handler = getActionHandle(session))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700213 {
Kun Yic892f4a2019-11-19 13:42:46 -0800214 return handler->stat(session, meta);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700215 }
Kun Yic892f4a2019-11-19 13:42:46 -0800216 return false;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700217}
218
219bool BlobManager::commit(uint16_t session, const std::vector<uint8_t>& data)
220{
Kun Yic892f4a2019-11-19 13:42:46 -0800221 if (auto handler = getActionHandle(session))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700222 {
Kun Yic892f4a2019-11-19 13:42:46 -0800223 return handler->commit(session, data);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700224 }
Kun Yic892f4a2019-11-19 13:42:46 -0800225 return false;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700226}
227
228bool BlobManager::close(uint16_t session)
229{
Kun Yic892f4a2019-11-19 13:42:46 -0800230 if (auto handler = getActionHandle(session))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700231 {
Kun Yic892f4a2019-11-19 13:42:46 -0800232 if (!handler->close(session))
233 {
234 return false;
235 }
Kun Yib61a88b2019-11-12 22:50:34 -0800236 eraseSession(handler, session);
Kun Yic892f4a2019-11-19 13:42:46 -0800237 return true;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700238 }
Kun Yic892f4a2019-11-19 13:42:46 -0800239 return false;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700240}
241
242std::vector<uint8_t> BlobManager::read(uint16_t session, uint32_t offset,
243 uint32_t requestedSize)
244{
Patrick Venture86c87f52019-02-01 14:44:15 -0800245 /* 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
Kun Yic892f4a2019-11-19 13:42:46 -0800259 if (auto handler = getActionHandle(session, OpenFlags::read))
260 {
261 return handler->read(session, offset,
262 std::min(maximumReadSize, requestedSize));
263 }
264 return {};
Patrick Ventureef3aead2018-09-12 08:53:29 -0700265}
266
267bool BlobManager::write(uint16_t session, uint32_t offset,
268 const std::vector<uint8_t>& data)
269{
Kun Yic892f4a2019-11-19 13:42:46 -0800270 if (auto handler = getActionHandle(session, OpenFlags::write))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700271 {
Kun Yic892f4a2019-11-19 13:42:46 -0800272 return handler->write(session, offset, data);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700273 }
Kun Yifea1d812019-11-14 11:38:44 -0800274 return false;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700275}
276
277bool 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. */
Kun Yi9ce83482019-11-14 13:08:39 -0800288 if (openFiles[path] > 0)
Patrick Ventureef3aead2018-09-12 08:53:29 -0700289 {
290 return false;
291 }
292
293 /* Try deleting it. */
294 return handler->deleteBlob(path);
295}
296
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700297bool BlobManager::writeMeta(uint16_t session, uint32_t offset,
298 const std::vector<uint8_t>& data)
299{
Kun Yic892f4a2019-11-19 13:42:46 -0800300 if (auto handler = getActionHandle(session))
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700301 {
Kun Yic892f4a2019-11-19 13:42:46 -0800302 return handler->writeMeta(session, offset, data);
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700303 }
Kun Yic892f4a2019-11-19 13:42:46 -0800304 return false;
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700305}
306
Patrick Ventureef3aead2018-09-12 08:53:29 -0700307bool BlobManager::getSession(uint16_t* sess)
308{
309 uint16_t tries = 0;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700310
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 {
Patrick Venturec9ad5ff2018-10-12 17:05:49 -0700323 uint16_t lsess = next++;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700324 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}
Patrick Ventureb3e07e22018-09-27 15:11:57 -0700334
335static std::unique_ptr<BlobManager> manager;
336
Patrick Venture73eb6872018-10-01 18:37:34 -0700337ManagerInterface* getBlobManager()
Patrick Ventureb3e07e22018-09-27 15:11:57 -0700338{
339 if (manager == nullptr)
340 {
341 manager = std::make_unique<BlobManager>();
342 }
343
344 return manager.get();
345}
346
Patrick Ventureef3aead2018-09-12 08:53:29 -0700347} // namespace blobs