1 /*
2  * Copyright 2018 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "handler.hpp"
18 
19 #include "flags.hpp"
20 #include "helper.hpp"
21 #include "status.hpp"
22 #include "tool_errors.hpp"
23 #include "util.hpp"
24 
25 #include <ipmiblob/blob_errors.hpp>
26 #include <stdplus/handle/managed.hpp>
27 
28 #include <algorithm>
29 #include <cstdint>
30 #include <cstring>
31 #include <string>
32 #include <vector>
33 
34 namespace host_tool
35 {
36 
37 static void closeBlob(uint16_t&& session, ipmiblob::BlobInterface*& blob)
38 {
39     blob->closeBlob(session);
40 }
41 
42 using BlobHandle =
43     stdplus::Managed<uint16_t, ipmiblob::BlobInterface*>::Handle<closeBlob>;
44 
45 template <typename... Args>
46 inline BlobHandle openBlob(ipmiblob::BlobInterface* blob, Args&&... args)
47 {
48     return BlobHandle(blob->openBlob(std::forward<Args>(args)...), blob);
49 }
50 
51 bool UpdateHandler::checkAvailable(const std::string& goalFirmware)
52 {
53     std::vector<std::string> blobs = blob->getBlobList();
54 
55     auto blobInst = std::find_if(
56         blobs.begin(), blobs.end(), [&goalFirmware](const std::string& iter) {
57             /* Running into weird scenarios where the string comparison doesn't
58              * work.  TODO: revisit.
59              */
60             return (0 == std::memcmp(goalFirmware.c_str(), iter.c_str(),
61                                      goalFirmware.length()));
62             // return (goalFirmware.compare(iter));
63         });
64     if (blobInst == blobs.end())
65     {
66         std::fprintf(stderr, "%s not found\n", goalFirmware.c_str());
67         return false;
68     }
69 
70     return true;
71 }
72 
73 void UpdateHandler::sendFile(const std::string& target, const std::string& path)
74 {
75     auto supported = handler->supportedType();
76 
77     try
78     {
79         auto session = openBlob(
80             blob, target,
81             static_cast<std::uint16_t>(supported) |
82                 static_cast<std::uint16_t>(
83                     ipmi_flash::FirmwareFlags::UpdateFlags::openWrite));
84 
85         if (!handler->sendContents(path, *session))
86         {
87             throw ToolException("Failed to send contents of " + path);
88         }
89     }
90     catch (const ipmiblob::BlobException& b)
91     {
92         throw ToolException("blob exception received: " +
93                             std::string(b.what()));
94     }
95 }
96 
97 bool UpdateHandler::verifyFile(const std::string& target, bool ignoreStatus)
98 {
99     try
100     {
101         auto session =
102             openBlob(blob, target,
103                      static_cast<std::uint16_t>(
104                          ipmi_flash::FirmwareFlags::UpdateFlags::openWrite));
105 
106         std::fprintf(stderr, "Committing to %s to trigger service\n",
107                      target.c_str());
108         blob->commit(*session, {});
109 
110         if (ignoreStatus)
111         {
112             // Skip checking the blob for status if ignoreStatus is enabled
113             return true;
114         }
115 
116         std::fprintf(stderr, "Calling stat on %s session to check status\n",
117                      target.c_str());
118         pollStatus(*session, blob);
119         return true;
120     }
121     catch (const ipmiblob::BlobException& b)
122     {
123         throw ToolException("blob exception received: " +
124                             std::string(b.what()));
125     }
126 }
127 
128 std::vector<uint8_t> UpdateHandler::readVersion(const std::string& versionBlob)
129 {
130     try
131     {
132         auto session =
133             openBlob(blob, versionBlob,
134                      static_cast<std::uint16_t>(
135                          ipmi_flash::FirmwareFlags::UpdateFlags::openRead));
136 
137         std::fprintf(stderr, "Calling stat on %s session to check status\n",
138                      versionBlob.c_str());
139 
140         /* TODO: call readBytes multiple times in case IPMI message length
141          * exceeds IPMI_MAX_MSG_LENGTH.
142          */
143         auto size = pollReadReady(*session, blob);
144         if (size > 0)
145         {
146             return blob->readBytes(*session, 0, size);
147         }
148         return {};
149     }
150     catch (const ipmiblob::BlobException& b)
151     {
152         throw ToolException("blob exception received: " +
153                             std::string(b.what()));
154     }
155 }
156 
157 void UpdateHandler::cleanArtifacts()
158 {
159     /* Errors aren't important for this call. */
160     try
161     {
162         std::fprintf(stderr, "Executing cleanup blob\n");
163         auto session =
164             openBlob(blob, ipmi_flash::cleanupBlobId,
165                      static_cast<std::uint16_t>(
166                          ipmi_flash::FirmwareFlags::UpdateFlags::openWrite));
167         blob->commit(*session, {});
168     }
169     catch (const std::exception& e)
170     {
171         std::fprintf(stderr, "Cleanup failed: %s\n", e.what());
172     }
173 }
174 
175 } // namespace host_tool
176