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     // TODO: stat should return the blob state and in the meta data information
87     // on whether a read is successful should be contained
88     // do things like determine if systemd target is triggered
89     // then check if file can be opened for read
90     return false; /* not yet implemented */
91 }
92 
93 bool VersionBlobHandler::open(uint16_t session, uint16_t flags,
94                               const std::string& path)
95 {
96     /* only reads are supported, check if blob is handled and make sure
97      * the blob isn't already opened
98      */
99     if (flags != blobs::read)
100     {
101         fprintf(stderr, "open %s fail: unsupported flags(0x%04X.)\n",
102                 path.c_str(), flags);
103         return false;
104     }
105 
106     auto info = std::make_unique<SessionInfo>();
107     info->blob = blobInfoMap.at(path).get();
108     info->blob->sessionsToUpdate.emplace(info.get());
109     if (info->blob->sessionsToUpdate.size() == 1 &&
110         !info->blob->actions->onOpen->trigger())
111     {
112         fprintf(stderr, "open %s fail: onOpen trigger failed\n", path.c_str());
113         info->blob->sessionsToUpdate.erase(info.get());
114         return false;
115     }
116 
117     sessionInfoMap[session] = std::move(info);
118     return true;
119 }
120 
121 std::vector<uint8_t> VersionBlobHandler::read(uint16_t session, uint32_t offset,
122                                               uint32_t requestedSize)
123 {
124     auto& data = sessionInfoMap.at(session)->data;
125     if (data == nullptr || !*data || (*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     return false;
155 }
156 
157 bool VersionBlobHandler::expire(uint16_t session)
158 {
159     close(session);
160     return true;
161 }
162 
163 } // namespace ipmi_flash
164