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/function_view.hpp> 27 #include <stdplus/handle/managed.hpp> 28 29 #include <algorithm> 30 #include <cstdint> 31 #include <cstring> 32 #include <string> 33 #include <vector> 34 35 namespace host_tool 36 { 37 38 static void closeBlob(uint16_t&& session, ipmiblob::BlobInterface*& blob) 39 { 40 blob->closeBlob(session); 41 } 42 43 using BlobHandle = 44 stdplus::Managed<uint16_t, ipmiblob::BlobInterface*>::Handle<closeBlob>; 45 46 template <typename... Args> 47 inline BlobHandle openBlob(ipmiblob::BlobInterface* blob, Args&&... args) 48 { 49 return BlobHandle(blob->openBlob(std::forward<Args>(args)...), blob); 50 } 51 52 bool UpdateHandler::checkAvailable(const std::string& goalFirmware) 53 { 54 std::vector<std::string> blobs = blob->getBlobList(); 55 56 auto blobInst = std::find_if(blobs.begin(), blobs.end(), 57 [&goalFirmware](const std::string& iter) { 58 /* Running into weird scenarios where the string comparison doesn't 59 * work. TODO: revisit. 60 */ 61 return (0 == std::memcmp(goalFirmware.c_str(), iter.c_str(), 62 goalFirmware.length())); 63 // return (goalFirmware.compare(iter)); 64 }); 65 if (blobInst == blobs.end()) 66 { 67 std::fprintf(stderr, "%s not found\n", goalFirmware.c_str()); 68 return false; 69 } 70 71 return true; 72 } 73 74 std::vector<uint8_t> UpdateHandler::retryIfFailed( 75 stdplus::function_view<std::vector<uint8_t>()> callback) 76 { 77 constexpr uint8_t retryCount = 3; 78 uint8_t i = 1; 79 while (true) 80 { 81 try 82 { 83 return callback(); 84 } 85 catch (const ipmiblob::BlobException& b) 86 { 87 throw ToolException("blob exception received: " + 88 std::string(b.what())); 89 } 90 catch (const ToolException& t) 91 { 92 uint8_t remains = retryCount - i; 93 std::fprintf( 94 stderr, 95 "tool exception received: %s: Retrying it %u more times\n", 96 t.what(), remains); 97 if (remains == 0) 98 throw; 99 } 100 ++i; 101 handler->waitForRetry(); 102 } 103 return {}; 104 } 105 106 void UpdateHandler::retrySendFile(const std::string& target, 107 const std::string& path) 108 { 109 auto supported = handler->supportedType(); 110 auto session = 111 openBlob(blob, target, 112 static_cast<std::uint16_t>(supported) | 113 static_cast<std::uint16_t>( 114 ipmi_flash::FirmwareFlags::UpdateFlags::openWrite)); 115 116 if (!handler->sendContents(path, *session)) 117 { 118 throw ToolException("Failed to send contents of " + path); 119 } 120 } 121 122 void UpdateHandler::sendFile(const std::string& target, const std::string& path) 123 { 124 retryIfFailed([this, target, path]() { 125 this->retrySendFile(target, path); 126 return std::vector<uint8_t>{}; 127 }); 128 } 129 130 void UpdateHandler::retryVerifyFile(const std::string& target, 131 bool ignoreStatus) 132 { 133 auto session = 134 openBlob(blob, target, 135 static_cast<std::uint16_t>( 136 ipmi_flash::FirmwareFlags::UpdateFlags::openWrite)); 137 138 std::fprintf(stderr, "Committing to %s to trigger service\n", 139 target.c_str()); 140 blob->commit(*session, {}); 141 142 if (ignoreStatus) 143 { 144 // Skip checking the blob for status if ignoreStatus is enabled 145 return; 146 } 147 148 std::fprintf(stderr, "Calling stat on %s session to check status\n", 149 target.c_str()); 150 pollStatus(*session, blob); 151 return; 152 } 153 154 bool UpdateHandler::verifyFile(const std::string& target, bool ignoreStatus) 155 { 156 retryIfFailed([this, target, ignoreStatus]() { 157 this->retryVerifyFile(target, ignoreStatus); 158 return std::vector<uint8_t>{}; 159 }); 160 161 return true; 162 } 163 164 std::vector<uint8_t> 165 UpdateHandler::retryReadVersion(const std::string& versionBlob) 166 { 167 auto session = 168 openBlob(blob, versionBlob, 169 static_cast<std::uint16_t>( 170 ipmi_flash::FirmwareFlags::UpdateFlags::openRead)); 171 172 std::fprintf(stderr, "Calling stat on %s session to check status\n", 173 versionBlob.c_str()); 174 175 /* TODO: call readBytes multiple times in case IPMI message length 176 * exceeds IPMI_MAX_MSG_LENGTH. 177 */ 178 auto size = pollReadReady(*session, blob); 179 if (size > 0) 180 { 181 return blob->readBytes(*session, 0, size); 182 } 183 return {}; 184 } 185 186 std::vector<uint8_t> UpdateHandler::readVersion(const std::string& versionBlob) 187 { 188 return retryIfFailed( 189 [this, versionBlob]() { return retryReadVersion(versionBlob); }); 190 } 191 192 void UpdateHandler::cleanArtifacts() 193 { 194 /* Errors aren't important for this call. */ 195 try 196 { 197 std::fprintf(stderr, "Executing cleanup blob\n"); 198 auto session = 199 openBlob(blob, ipmi_flash::cleanupBlobId, 200 static_cast<std::uint16_t>( 201 ipmi_flash::FirmwareFlags::UpdateFlags::openWrite)); 202 blob->commit(*session, {}); 203 } 204 catch (const std::exception& e) 205 { 206 std::fprintf(stderr, "Cleanup failed: %s\n", e.what()); 207 } 208 } 209 210 } // namespace host_tool 211