xref: /openbmc/phosphor-ipmi-flash/bmc/version-handler/version_handler.cpp (revision bec23189399cf5524352a4a42debd9ed43b31bfa)
1 #include "version_handler.hpp"
2 
3 #include <utility>
4 #include <vector>
5 
6 namespace ipmi_flash
7 {
8 
9 VersionBlobHandler::VersionBlobHandler(
10     std::vector<HandlerConfig<ActionPack>>&& configs)
11 {
12     for (auto& config : configs)
13     {
14         auto info = std::make_unique<BlobInfo>();
15         info->blobId = std::move(config.blobId);
16         info->actions = std::move(config.actions);
17         info->handler = std::move(config.handler);
18         info->actions->onOpen->setCallback(
19             [infoP = info.get()](TriggerableActionInterface& tai) {
20                 auto data =
21                     std::make_shared<std::optional<std::vector<uint8_t>>>();
22                 do
23                 {
24                     if (tai.status() != ActionStatus::success)
25                     {
26                         fprintf(stderr, "Version file unit failed for %s\n",
27                                 infoP->blobId.c_str());
28                         continue;
29                     }
30                     if (!infoP->handler->open("", std::ios::in))
31                     {
32                         fprintf(stderr, "Opening version file failed for %s\n",
33                                 infoP->blobId.c_str());
34                         continue;
35                     }
36                     auto d = infoP->handler->read(
37                         0, std::numeric_limits<uint32_t>::max());
38                     infoP->handler->close();
39                     if (!d)
40                     {
41                         fprintf(stderr, "Reading version file failed for %s\n",
42                                 infoP->blobId.c_str());
43                         continue;
44                     }
45                     *data = std::move(d);
46                 } while (false);
47                 for (auto sessionP : infoP->sessionsToUpdate)
48                 {
49                     sessionP->data = data;
50                 }
51                 infoP->sessionsToUpdate.clear();
52             });
53         if (!blobInfoMap.try_emplace(info->blobId, std::move(info)).second)
54         {
55             fprintf(stderr, "Ignoring duplicate config for %s\n",
56                     info->blobId.c_str());
57         }
58     }
59 }
60 
61 bool VersionBlobHandler::canHandleBlob(const std::string& path)
62 {
63     return blobInfoMap.find(path) != blobInfoMap.end();
64 }
65 
66 std::vector<std::string> VersionBlobHandler::getBlobIds()
67 {
68     std::vector<std::string> ret;
69     for (const auto& [key, _] : blobInfoMap)
70     {
71         ret.emplace_back(key);
72     }
73     return ret;
74 }
75 
76 /**
77  * deleteBlob - does nothing, always fails
78  */
79 bool VersionBlobHandler::deleteBlob(const std::string& path)
80 {
81     return false;
82 }
83 
84 bool VersionBlobHandler::stat(const std::string& path, blobs::BlobMeta* meta)
85 {
86     return false;
87 }
88 
89 bool VersionBlobHandler::open(uint16_t session, uint16_t flags,
90                               const std::string& path)
91 {
92     /* only reads are supported, check if blob is handled and make sure
93      * the blob isn't already opened
94      */
95     if (flags != blobs::read)
96     {
97         fprintf(stderr, "open %s fail: unsupported flags(0x%04X.)\n",
98                 path.c_str(), flags);
99         return false;
100     }
101 
102     auto info = std::make_unique<SessionInfo>();
103     info->blob = blobInfoMap.at(path).get();
104     info->blob->sessionsToUpdate.emplace(info.get());
105     if (info->blob->sessionsToUpdate.size() == 1 &&
106         !info->blob->actions->onOpen->trigger())
107     {
108         fprintf(stderr, "open %s fail: onOpen trigger failed\n", path.c_str());
109         info->blob->sessionsToUpdate.erase(info.get());
110         return false;
111     }
112 
113     sessionInfoMap[session] = std::move(info);
114     return true;
115 }
116 
117 std::vector<uint8_t> VersionBlobHandler::read(uint16_t session, uint32_t offset,
118                                               uint32_t requestedSize)
119 {
120     auto& data = sessionInfoMap.at(session)->data;
121     if (data == nullptr || !*data)
122     {
123         throw std::runtime_error("Version data not ready for read");
124     }
125     if ((*data)->size() < offset)
126     {
127         return {};
128     }
129     std::vector<uint8_t> ret(
130         std::min<size_t>(requestedSize, (*data)->size() - offset));
131     std::memcpy(&ret[0], &(**data)[offset], ret.size());
132     return ret;
133 }
134 
135 bool VersionBlobHandler::close(uint16_t session)
136 {
137     auto it = sessionInfoMap.find(session);
138     if (it == sessionInfoMap.end())
139     {
140         return false;
141     }
142     auto& info = *it->second;
143     info.blob->sessionsToUpdate.erase(&info);
144     if (info.blob->sessionsToUpdate.empty())
145     {
146         info.blob->actions->onOpen->abort();
147     }
148     sessionInfoMap.erase(it);
149     return true;
150 }
151 
152 bool VersionBlobHandler::stat(uint16_t session, blobs::BlobMeta* meta)
153 {
154     const auto& data = sessionInfoMap.at(session)->data;
155     if (data == nullptr)
156     {
157         meta->blobState = blobs::StateFlags::committing;
158         meta->size = 0;
159     }
160     else if (!*data)
161     {
162         meta->blobState = blobs::StateFlags::commit_error;
163         meta->size = 0;
164     }
165     else
166     {
167         meta->blobState =
168             blobs::StateFlags::committed | blobs::StateFlags::open_read;
169         meta->size = (*data)->size();
170     }
171     return true;
172 }
173 
174 bool VersionBlobHandler::expire(uint16_t session)
175 {
176     close(session);
177     return true;
178 }
179 
180 } // namespace ipmi_flash
181