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