blob: 64cc08f54d67038345e70b5124b53cd92b6aaa1b [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 Yibeee9e52019-11-22 20:01:28 -080030 if (auto item = sessions.find(session); item != sessions.end())
Kun Yi9ce83482019-11-14 13:08:39 -080031 {
Kun Yibeee9e52019-11-22 20:01:28 -080032 const auto& blobId = item->second.blobId;
33
34 /* Ok for openSessions[handler] to be an empty set */
35 openSessions[handler].erase(session);
36 openFiles[blobId]--;
37 if (openFiles[blobId] == 0)
38 {
39 openFiles.erase(blobId);
40 }
41
42 /* Erase at the end after using the session info */
43 sessions.erase(session);
Kun Yi9ce83482019-11-14 13:08:39 -080044 }
Kun Yib61a88b2019-11-12 22:50:34 -080045}
46
47void BlobManager::cleanUpStaleSessions(GenericBlobInterface* handler)
48{
49 if (openSessions.count(handler) == 0)
50 {
51 return;
52 }
53
54 auto timeNow = std::chrono::steady_clock::now();
55 std::set<uint16_t> expiredSet;
56
57 for (auto sessionId : openSessions[handler])
58 {
59 if (timeNow - sessions[sessionId].lastActionTime >= sessionTimeout)
60 {
61 expiredSet.insert(sessionId);
62 }
63 }
64
65 for (auto sessionId : expiredSet)
66 {
67 std::cerr << "phosphor-ipmi-blobs: expiring stale session " << sessionId
68 << std::endl;
69
70 /* We do a best case recovery by issuing an expire call. If it fails
71 * don't erase sessions since the handler side might be still tracking
72 * it as open. */
73 if (handler->expire(sessionId))
74 {
75 eraseSession(handler, sessionId);
76 }
77 else
78 {
79 std::cerr << "phosphor-ipmi-blobs: failed to expire session "
80 << sessionId << std::endl;
81 }
82 }
83}
84
Patrick Ventureef3aead2018-09-12 08:53:29 -070085bool BlobManager::registerHandler(std::unique_ptr<GenericBlobInterface> handler)
86{
87 if (!handler)
88 {
89 return false;
90 }
91
92 handlers.push_back(std::move(handler));
93 return true;
94}
95
96uint32_t BlobManager::buildBlobList()
97{
98 /* Clear out the current list (IPMI handler is presently single-threaded).
99 */
100 ids.clear();
101
102 /* Grab the list of blobs and extend the local list */
Patrick Venturea6e21a02018-10-23 09:45:04 -0700103 for (const auto& h : handlers)
Patrick Ventureef3aead2018-09-12 08:53:29 -0700104 {
105 std::vector<std::string> blobs = h->getBlobIds();
106 ids.insert(ids.end(), blobs.begin(), blobs.end());
107 }
108
109 return ids.size();
110}
111
112std::string BlobManager::getBlobId(uint32_t index)
113{
114 /* Range check. */
115 if (index >= ids.size())
116 {
117 return "";
118 }
119
120 return ids[index];
121}
122
123bool BlobManager::open(uint16_t flags, const std::string& path,
124 uint16_t* session)
125{
126 GenericBlobInterface* handler = getHandler(path);
127
128 /* No handler found. */
129 if (!handler)
130 {
131 return false;
132 }
133
Patrick Venture2f581512019-01-10 09:30:36 -0800134 /* No sessions available... */
Patrick Ventureef3aead2018-09-12 08:53:29 -0700135 if (!getSession(session))
136 {
137 return false;
138 }
139
140 /* Verify flags - must be at least read or write */
141 if (!(flags & (OpenFlags::read | OpenFlags::write)))
142 {
143 /* Neither read not write set, which means calls to Read/Write will
144 * reject. */
145 return false;
146 }
147
Kun Yib61a88b2019-11-12 22:50:34 -0800148 /* Try to clean up anything that's falling out of cleanup timeout for this
149 * handler */
150 cleanUpStaleSessions(handler);
151
Patrick Ventureef3aead2018-09-12 08:53:29 -0700152 if (!handler->open(*session, flags, path))
153 {
154 return false;
155 }
156
157 /* Associate session with handler */
158 sessions[*session] = SessionInfo(path, handler, flags);
Kun Yib61a88b2019-11-12 22:50:34 -0800159 openSessions[handler].insert(*session);
Kun Yi9ce83482019-11-14 13:08:39 -0800160 openFiles[path]++;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700161 return true;
162}
163
164GenericBlobInterface* BlobManager::getHandler(const std::string& path)
165{
166 /* Find a handler. */
Patrick Venturee50d4e42018-10-23 09:50:27 -0700167 auto h = std::find_if(
168 handlers.begin(), handlers.end(),
169 [&path](const auto& iter) { return (iter->canHandleBlob(path)); });
170 if (h != handlers.end())
Patrick Ventureef3aead2018-09-12 08:53:29 -0700171 {
Patrick Venturee50d4e42018-10-23 09:50:27 -0700172 return h->get();
Patrick Ventureef3aead2018-09-12 08:53:29 -0700173 }
174
Patrick Venturee50d4e42018-10-23 09:50:27 -0700175 return nullptr;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700176}
177
Kun Yifea1d812019-11-14 11:38:44 -0800178GenericBlobInterface* BlobManager::getActionHandle(uint16_t session,
179 uint16_t requiredFlags)
180{
181 if (auto item = sessions.find(session);
182 item != sessions.end() && (item->second.flags & requiredFlags))
183 {
184 item->second.lastActionTime = std::chrono::steady_clock::now();
185 return item->second.handler;
186 }
187 return nullptr;
188}
189
Patrick Venture8bc11772019-06-04 07:20:24 -0700190bool BlobManager::stat(const std::string& path, BlobMeta* meta)
Patrick Ventureef3aead2018-09-12 08:53:29 -0700191{
192 /* meta should never be NULL. */
193 GenericBlobInterface* handler = getHandler(path);
194
195 /* No handler found. */
196 if (!handler)
197 {
198 return false;
199 }
200
201 return handler->stat(path, meta);
202}
203
Patrick Venture8bc11772019-06-04 07:20:24 -0700204bool BlobManager::stat(uint16_t session, BlobMeta* meta)
Patrick Ventureef3aead2018-09-12 08:53:29 -0700205{
Kun Yic892f4a2019-11-19 13:42:46 -0800206 if (auto handler = getActionHandle(session))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700207 {
Kun Yic892f4a2019-11-19 13:42:46 -0800208 return handler->stat(session, meta);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700209 }
Kun Yic892f4a2019-11-19 13:42:46 -0800210 return false;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700211}
212
213bool BlobManager::commit(uint16_t session, const std::vector<uint8_t>& data)
214{
Kun Yic892f4a2019-11-19 13:42:46 -0800215 if (auto handler = getActionHandle(session))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700216 {
Kun Yic892f4a2019-11-19 13:42:46 -0800217 return handler->commit(session, data);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700218 }
Kun Yic892f4a2019-11-19 13:42:46 -0800219 return false;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700220}
221
222bool BlobManager::close(uint16_t session)
223{
Kun Yic892f4a2019-11-19 13:42:46 -0800224 if (auto handler = getActionHandle(session))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700225 {
Kun Yic892f4a2019-11-19 13:42:46 -0800226 if (!handler->close(session))
227 {
228 return false;
229 }
Kun Yib61a88b2019-11-12 22:50:34 -0800230 eraseSession(handler, session);
Kun Yic892f4a2019-11-19 13:42:46 -0800231 return true;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700232 }
Kun Yic892f4a2019-11-19 13:42:46 -0800233 return false;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700234}
235
236std::vector<uint8_t> BlobManager::read(uint16_t session, uint32_t offset,
237 uint32_t requestedSize)
238{
Patrick Venture86c87f52019-02-01 14:44:15 -0800239 /* TODO: Currently, configure_ac isn't finding libuserlayer, w.r.t the
240 * symbols I need.
241 */
242
243 /** The channel to use for now.
244 * TODO: We will receive this information through the IPMI message call.
245 */
246 // const int ipmiChannel = ipmi::currentChNum;
247 /** This information is transport specific.
248 * TODO: We need a way to know this dynamically.
249 * on BT, 4 bytes of header, and 1 reply code.
250 */
251 // uint32_t maxTransportSize = ipmi::getChannelMaxTransferSize(ipmiChannel);
252
Kun Yic892f4a2019-11-19 13:42:46 -0800253 if (auto handler = getActionHandle(session, OpenFlags::read))
254 {
255 return handler->read(session, offset,
256 std::min(maximumReadSize, requestedSize));
257 }
258 return {};
Patrick Ventureef3aead2018-09-12 08:53:29 -0700259}
260
261bool BlobManager::write(uint16_t session, uint32_t offset,
262 const std::vector<uint8_t>& data)
263{
Kun Yic892f4a2019-11-19 13:42:46 -0800264 if (auto handler = getActionHandle(session, OpenFlags::write))
Patrick Ventureef3aead2018-09-12 08:53:29 -0700265 {
Kun Yic892f4a2019-11-19 13:42:46 -0800266 return handler->write(session, offset, data);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700267 }
Kun Yifea1d812019-11-14 11:38:44 -0800268 return false;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700269}
270
271bool BlobManager::deleteBlob(const std::string& path)
272{
273 GenericBlobInterface* handler = getHandler(path);
274
275 /* No handler found. */
276 if (!handler)
277 {
278 return false;
279 }
280
281 /* Check if the file has any open handles. */
Kun Yi9ce83482019-11-14 13:08:39 -0800282 if (openFiles[path] > 0)
Patrick Ventureef3aead2018-09-12 08:53:29 -0700283 {
284 return false;
285 }
286
287 /* Try deleting it. */
288 return handler->deleteBlob(path);
289}
290
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700291bool BlobManager::writeMeta(uint16_t session, uint32_t offset,
292 const std::vector<uint8_t>& data)
293{
Kun Yic892f4a2019-11-19 13:42:46 -0800294 if (auto handler = getActionHandle(session))
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700295 {
Kun Yic892f4a2019-11-19 13:42:46 -0800296 return handler->writeMeta(session, offset, data);
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700297 }
Kun Yic892f4a2019-11-19 13:42:46 -0800298 return false;
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700299}
300
Patrick Ventureef3aead2018-09-12 08:53:29 -0700301bool BlobManager::getSession(uint16_t* sess)
302{
303 uint16_t tries = 0;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700304
305 if (!sess)
306 {
307 return false;
308 }
309
310 /* This is not meant to fail as you have 64KiB values available. */
311
312 /* TODO(venture): We could just count the keys in the session map to know
313 * if it's full.
314 */
315 do
316 {
Patrick Venturec9ad5ff2018-10-12 17:05:49 -0700317 uint16_t lsess = next++;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700318 if (!sessions.count(lsess))
319 {
320 /* value not in use, return it. */
321 (*sess) = lsess;
322 return true;
323 }
324 } while (++tries < 0xffff);
325
326 return false;
327}
Patrick Ventureb3e07e22018-09-27 15:11:57 -0700328
329static 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