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 <algorithm>
26 #include <cstdint>
27 #include <cstring>
28 #include <ipmiblob/blob_errors.hpp>
29 #include <string>
30 #include <vector>
31 
32 namespace host_tool
33 {
34 
35 bool UpdateHandler::checkAvailable(const std::string& goalFirmware)
36 {
37     std::vector<std::string> blobs = blob->getBlobList();
38 
39     auto blobInst = std::find_if(
40         blobs.begin(), blobs.end(), [&goalFirmware](const std::string& iter) {
41             /* Running into weird scenarios where the string comparison doesn't
42              * work.  TODO: revisit.
43              */
44             return (0 == std::memcmp(goalFirmware.c_str(), iter.c_str(),
45                                      goalFirmware.length()));
46             // return (goalFirmware.compare(iter));
47         });
48     if (blobInst == blobs.end())
49     {
50         std::fprintf(stderr, "%s not found\n", goalFirmware.c_str());
51         return false;
52     }
53 
54     return true;
55 }
56 
57 void UpdateHandler::sendFile(const std::string& target, const std::string& path)
58 {
59     std::uint16_t session;
60     auto supported = handler->supportedType();
61 
62     try
63     {
64         session = blob->openBlob(
65             target, static_cast<std::uint16_t>(supported) |
66                         static_cast<std::uint16_t>(
67                             ipmi_flash::FirmwareFlags::UpdateFlags::openWrite));
68     }
69     catch (const ipmiblob::BlobException& b)
70     {
71         throw ToolException("blob exception received: " +
72                             std::string(b.what()));
73     }
74 
75     if (!handler->sendContents(path, session))
76     {
77         /* Need to close the session on failure, or it's stuck open (until the
78          * blob handler timeout is implemented, and even then, why make it wait.
79          */
80         blob->closeBlob(session);
81         throw ToolException("Failed to send contents of " + path);
82     }
83 
84     blob->closeBlob(session);
85 }
86 
87 bool UpdateHandler::verifyFile(const std::string& target, bool ignoreStatus)
88 {
89     std::uint16_t session;
90     bool success = false;
91 
92     try
93     {
94         session = blob->openBlob(
95             target, static_cast<std::uint16_t>(
96                         ipmi_flash::FirmwareFlags::UpdateFlags::openWrite));
97     }
98     catch (const ipmiblob::BlobException& b)
99     {
100         throw ToolException("blob exception received: " +
101                             std::string(b.what()));
102     }
103 
104     std::fprintf(stderr, "Committing to %s to trigger service\n",
105                  target.c_str());
106 
107     try
108     {
109         blob->commit(session, {});
110     }
111     catch (const ipmiblob::BlobException& b)
112     {
113         blob->closeBlob(session);
114         throw ToolException("blob exception received: " +
115                             std::string(b.what()));
116     }
117 
118     if (ignoreStatus)
119     {
120         // Skip checking the blob for status if ignoreStatus is enabled
121         blob->closeBlob(session);
122         return true;
123     }
124 
125     std::fprintf(stderr, "Calling stat on %s session to check status\n",
126                  target.c_str());
127 
128     if (pollStatus(session, blob))
129     {
130         std::fprintf(stderr, "Returned success\n");
131         success = true;
132     }
133     else
134     {
135         std::fprintf(stderr, "Returned non-success (could still "
136                              "be running (unlikely))\n");
137     }
138 
139     blob->closeBlob(session);
140     return (success == true);
141 }
142 
143 void UpdateHandler::cleanArtifacts()
144 {
145     /* open(), commit(), close() */
146     std::uint16_t session;
147 
148     /* Errors aren't important for this call. */
149     try
150     {
151         std::fprintf(stderr, "Opening the cleanup blob\n");
152         session = blob->openBlob(
153             ipmi_flash::cleanupBlobId,
154             static_cast<std::uint16_t>(
155                 ipmi_flash::FirmwareFlags::UpdateFlags::openWrite));
156     }
157     catch (...)
158     {
159         return;
160     }
161 
162     try
163     {
164         std::fprintf(stderr, "Committing to the cleanup blob\n");
165         blob->commit(session, {});
166         std::fprintf(stderr, "Closing cleanup blob\n");
167     }
168     catch (...)
169     {
170     }
171 
172     blob->closeBlob(session);
173 }
174 
175 } // namespace host_tool
176