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 
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 =
40                     std::make_shared<std::optional<std::vector<uint8_t>>>();
41                 do
42                 {
43                     if (tai.status() != ActionStatus::success)
44                     {
45                         fprintf(stderr,
46                                 "LogBlobHandler: Log file unit failed for %s\n",
47                                 infoP->blobId.c_str());
48                         continue;
49                     }
50                     if (!infoP->handler->open("", std::ios::in))
51                     {
52                         fprintf(
53                             stderr,
54                             "LogBlobHandler: Opening log file failed for %s\n",
55                             infoP->blobId.c_str());
56                         continue;
57                     }
58                     auto d = infoP->handler->read(
59                         0, std::numeric_limits<uint32_t>::max());
60                     infoP->handler->close();
61                     if (!d)
62                     {
63                         fprintf(
64                             stderr,
65                             "LogBlobHandler: Reading log file failed for %s\n",
66                             infoP->blobId.c_str());
67                         continue;
68                     }
69                     *data = std::move(d);
70                 } while (false);
71                 for (auto sessionP : infoP->sessionsToUpdate)
72                 {
73                     sessionP->data = data;
74                 }
75                 infoP->sessionsToUpdate.clear();
76             });
77         if (!blobInfoMap.try_emplace(info->blobId, std::move(info)).second)
78         {
79             fprintf(stderr,
80                     "LogBlobHandler: Ignoring duplicate config for %s\n",
81                     info->blobId.c_str());
82         }
83     }
84 }
85 
86 bool LogBlobHandler::canHandleBlob(const std::string& path)
87 {
88     return blobInfoMap.find(path) != blobInfoMap.end();
89 }
90 
91 std::vector<std::string> LogBlobHandler::getBlobIds()
92 {
93     std::vector<std::string> ret;
94     for (const auto& [key, _] : blobInfoMap)
95     {
96         ret.emplace_back(key);
97     }
98     return ret;
99 }
100 
101 /**
102  * deleteBlob - does nothing, always fails
103  */
104 bool LogBlobHandler::deleteBlob(const std::string& path)
105 {
106     for (const auto& [sessionId, sessionInfo] : sessionInfoMap)
107     {
108         if (sessionInfo->blob->blobId == path)
109         {
110             fprintf(stderr,
111                     "LogBlobHandler: delete %s fail: there is an open session "
112                     "for this blob\n",
113                     path.c_str());
114             return false;
115         }
116     }
117 
118     auto* blob = blobInfoMap.at(path).get();
119     if (!blob->actions->onDelete->trigger())
120     {
121         fprintf(stderr,
122                 "LogBlobHandler: delete %s fail: onDelete trigger failed\n",
123                 path.c_str());
124         return false;
125     }
126     return true;
127 }
128 
129 bool LogBlobHandler::stat(const std::string&, blobs::BlobMeta*)
130 {
131     return false;
132 }
133 
134 bool LogBlobHandler::open(uint16_t session, uint16_t flags,
135                           const std::string& path)
136 {
137     /* only reads are supported, check if blob is handled and make sure
138      * the blob isn't already opened
139      */
140     if (flags != blobs::read)
141     {
142         fprintf(stderr,
143                 "LogBlobHandler: open %s fail: unsupported flags(0x%04X.)\n",
144                 path.c_str(), flags);
145         return false;
146     }
147 
148     auto info = std::make_unique<SessionInfo>();
149     info->blob = blobInfoMap.at(path).get();
150     info->blob->sessionsToUpdate.emplace(info.get());
151     if (info->blob->sessionsToUpdate.size() == 1 &&
152         !info->blob->actions->onOpen->trigger())
153     {
154         fprintf(stderr, "LogBlobHandler: open %s fail: onOpen trigger failed\n",
155                 path.c_str());
156         info->blob->sessionsToUpdate.erase(info.get());
157         return false;
158     }
159 
160     sessionInfoMap[session] = std::move(info);
161     return true;
162 }
163 
164 std::vector<uint8_t> LogBlobHandler::read(uint16_t session, uint32_t offset,
165                                           uint32_t requestedSize)
166 {
167     auto& data = sessionInfoMap.at(session)->data;
168     if (data == nullptr || !*data)
169     {
170         throw std::runtime_error("LogBlobHandler: Log data not ready for read");
171     }
172     if ((*data)->size() < offset)
173     {
174         return {};
175     }
176     std::vector<uint8_t> ret(
177         std::min<size_t>(requestedSize, (*data)->size() - offset));
178     std::memcpy(&ret[0], &(**data)[offset], ret.size());
179     return ret;
180 }
181 
182 bool LogBlobHandler::close(uint16_t session)
183 {
184     auto it = sessionInfoMap.find(session);
185     if (it == sessionInfoMap.end())
186     {
187         return false;
188     }
189     auto& info = *it->second;
190     info.blob->sessionsToUpdate.erase(&info);
191     if (info.blob->sessionsToUpdate.empty())
192     {
193         info.blob->actions->onOpen->abort();
194     }
195     sessionInfoMap.erase(it);
196     return true;
197 }
198 
199 bool LogBlobHandler::stat(uint16_t session, blobs::BlobMeta* meta)
200 {
201     const auto& data = sessionInfoMap.at(session)->data;
202     if (data == nullptr)
203     {
204         meta->blobState = blobs::StateFlags::committing;
205         meta->size = 0;
206     }
207     else if (!*data)
208     {
209         meta->blobState = blobs::StateFlags::commit_error;
210         meta->size = 0;
211     }
212     else
213     {
214         meta->blobState = blobs::StateFlags::committed |
215                           blobs::StateFlags::open_read;
216         meta->size = (*data)->size();
217     }
218     return true;
219 }
220 
221 bool LogBlobHandler::expire(uint16_t session)
222 {
223     close(session);
224     return true;
225 }
226 
227 } // namespace ipmi_flash
228