1 // Copyright 2021 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "log_handler.hpp"
16
17 #include <algorithm>
18 #include <cstring>
19 #include <ios>
20 #include <limits>
21 #include <memory>
22 #include <optional>
23 #include <utility>
24 #include <vector>
25
26 namespace ipmi_flash
27 {
28
LogBlobHandler(std::vector<HandlerConfig<ActionPack>> && configs)29 LogBlobHandler::LogBlobHandler(std::vector<HandlerConfig<ActionPack>>&& configs)
30 {
31 for (auto& config : configs)
32 {
33 auto info = std::make_unique<BlobInfo>();
34 info->blobId = std::move(config.blobId);
35 info->actions = std::move(config.actions);
36 info->handler = std::move(config.handler);
37 info->actions->onOpen->setCallback(
38 [infoP = info.get()](TriggerableActionInterface& tai) {
39 auto data = std::make_shared<std::optional<std::vector<uint8_t>>>();
40 do
41 {
42 if (tai.status() != ActionStatus::success)
43 {
44 fprintf(stderr,
45 "LogBlobHandler: Log file unit failed for %s\n",
46 infoP->blobId.c_str());
47 continue;
48 }
49 if (!infoP->handler->open("", std::ios::in))
50 {
51 fprintf(stderr,
52 "LogBlobHandler: Opening log file failed for %s\n",
53 infoP->blobId.c_str());
54 continue;
55 }
56 auto d = infoP->handler->read(
57 0, std::numeric_limits<uint32_t>::max());
58 infoP->handler->close();
59 if (!d)
60 {
61 fprintf(stderr,
62 "LogBlobHandler: Reading log file failed for %s\n",
63 infoP->blobId.c_str());
64 continue;
65 }
66 *data = std::move(d);
67 } while (false);
68 for (auto sessionP : infoP->sessionsToUpdate)
69 {
70 sessionP->data = data;
71 }
72 infoP->sessionsToUpdate.clear();
73 });
74 if (!blobInfoMap.try_emplace(info->blobId, std::move(info)).second)
75 {
76 fprintf(stderr,
77 "LogBlobHandler: Ignoring duplicate config for %s\n",
78 info->blobId.c_str());
79 }
80 }
81 }
82
canHandleBlob(const std::string & path)83 bool LogBlobHandler::canHandleBlob(const std::string& path)
84 {
85 return blobInfoMap.find(path) != blobInfoMap.end();
86 }
87
getBlobIds()88 std::vector<std::string> LogBlobHandler::getBlobIds()
89 {
90 std::vector<std::string> ret;
91 for (const auto& [key, _] : blobInfoMap)
92 {
93 ret.emplace_back(key);
94 }
95 return ret;
96 }
97
98 /**
99 * deleteBlob - does nothing, always fails
100 */
deleteBlob(const std::string & path)101 bool LogBlobHandler::deleteBlob(const std::string& path)
102 {
103 for (const auto& [sessionId, sessionInfo] : sessionInfoMap)
104 {
105 if (sessionInfo->blob->blobId == path)
106 {
107 fprintf(stderr,
108 "LogBlobHandler: delete %s fail: there is an open session "
109 "for this blob\n",
110 path.c_str());
111 return false;
112 }
113 }
114
115 auto* blob = blobInfoMap.at(path).get();
116 if (!blob->actions->onDelete->trigger())
117 {
118 fprintf(stderr,
119 "LogBlobHandler: delete %s fail: onDelete trigger failed\n",
120 path.c_str());
121 return false;
122 }
123 return true;
124 }
125
stat(const std::string &,blobs::BlobMeta *)126 bool LogBlobHandler::stat(const std::string&, blobs::BlobMeta*)
127 {
128 return false;
129 }
130
open(uint16_t session,uint16_t flags,const std::string & path)131 bool LogBlobHandler::open(uint16_t session, uint16_t flags,
132 const std::string& path)
133 {
134 /* only reads are supported, check if blob is handled and make sure
135 * the blob isn't already opened
136 */
137 if (flags != blobs::read)
138 {
139 fprintf(stderr,
140 "LogBlobHandler: open %s fail: unsupported flags(0x%04X.)\n",
141 path.c_str(), flags);
142 return false;
143 }
144
145 auto info = std::make_unique<SessionInfo>();
146 info->blob = blobInfoMap.at(path).get();
147 info->blob->sessionsToUpdate.emplace(info.get());
148 if (info->blob->sessionsToUpdate.size() == 1 &&
149 !info->blob->actions->onOpen->trigger())
150 {
151 fprintf(stderr, "LogBlobHandler: open %s fail: onOpen trigger failed\n",
152 path.c_str());
153 info->blob->sessionsToUpdate.erase(info.get());
154 return false;
155 }
156
157 sessionInfoMap[session] = std::move(info);
158 return true;
159 }
160
read(uint16_t session,uint32_t offset,uint32_t requestedSize)161 std::vector<uint8_t> LogBlobHandler::read(uint16_t session, uint32_t offset,
162 uint32_t requestedSize)
163 {
164 auto& data = sessionInfoMap.at(session)->data;
165 if (data == nullptr || !*data)
166 {
167 throw std::runtime_error("LogBlobHandler: Log data not ready for read");
168 }
169 if ((*data)->size() < offset)
170 {
171 return {};
172 }
173 std::vector<uint8_t> ret(
174 std::min<size_t>(requestedSize, (*data)->size() - offset));
175 std::memcpy(&ret[0], &(**data)[offset], ret.size());
176 return ret;
177 }
178
close(uint16_t session)179 bool LogBlobHandler::close(uint16_t session)
180 {
181 auto it = sessionInfoMap.find(session);
182 if (it == sessionInfoMap.end())
183 {
184 return false;
185 }
186 auto& info = *it->second;
187 info.blob->sessionsToUpdate.erase(&info);
188 if (info.blob->sessionsToUpdate.empty())
189 {
190 info.blob->actions->onOpen->abort();
191 }
192 sessionInfoMap.erase(it);
193 return true;
194 }
195
stat(uint16_t session,blobs::BlobMeta * meta)196 bool LogBlobHandler::stat(uint16_t session, blobs::BlobMeta* meta)
197 {
198 const auto& data = sessionInfoMap.at(session)->data;
199 if (data == nullptr)
200 {
201 meta->blobState = blobs::StateFlags::committing;
202 meta->size = 0;
203 }
204 else if (!*data)
205 {
206 meta->blobState = blobs::StateFlags::commit_error;
207 meta->size = 0;
208 }
209 else
210 {
211 meta->blobState = blobs::StateFlags::committed |
212 blobs::StateFlags::open_read;
213 meta->size = (*data)->size();
214 }
215 return true;
216 }
217
expire(uint16_t session)218 bool LogBlobHandler::expire(uint16_t session)
219 {
220 close(session);
221 return true;
222 }
223
224 } // namespace ipmi_flash
225