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