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(blobs.begin(), blobs.end(), 56 [&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::retrySendFile(const std::string& target, 74 const std::string& path) 75 { 76 auto supported = handler->supportedType(); 77 auto session = 78 openBlob(blob, target, 79 static_cast<std::uint16_t>(supported) | 80 static_cast<std::uint16_t>( 81 ipmi_flash::FirmwareFlags::UpdateFlags::openWrite)); 82 83 if (!handler->sendContents(path, *session)) 84 { 85 throw ToolException("Failed to send contents of " + path); 86 } 87 } 88 89 void UpdateHandler::sendFile(const std::string& target, const std::string& path) 90 { 91 const uint8_t retryCount = 3; 92 uint8_t i = 1; 93 while (true) 94 { 95 try 96 { 97 retrySendFile(target, path); 98 return; 99 } 100 catch (const ipmiblob::BlobException& b) 101 { 102 throw ToolException("blob exception received: " + 103 std::string(b.what())); 104 } 105 catch (const ToolException& t) 106 { 107 uint8_t remains = retryCount - i; 108 std::fprintf( 109 stderr, 110 "tool exception received: %s: Retrying it %u more times\n", 111 t.what(), remains); 112 if (remains == 0) 113 throw; 114 } 115 ++i; 116 } 117 } 118 119 bool UpdateHandler::verifyFile(const std::string& target, bool ignoreStatus) 120 { 121 try 122 { 123 auto session = 124 openBlob(blob, target, 125 static_cast<std::uint16_t>( 126 ipmi_flash::FirmwareFlags::UpdateFlags::openWrite)); 127 128 std::fprintf(stderr, "Committing to %s to trigger service\n", 129 target.c_str()); 130 blob->commit(*session, {}); 131 132 if (ignoreStatus) 133 { 134 // Skip checking the blob for status if ignoreStatus is enabled 135 return true; 136 } 137 138 std::fprintf(stderr, "Calling stat on %s session to check status\n", 139 target.c_str()); 140 pollStatus(*session, blob); 141 return true; 142 } 143 catch (const ipmiblob::BlobException& b) 144 { 145 throw ToolException("blob exception received: " + 146 std::string(b.what())); 147 } 148 } 149 150 std::vector<uint8_t> UpdateHandler::readVersion(const std::string& versionBlob) 151 { 152 try 153 { 154 auto session = 155 openBlob(blob, versionBlob, 156 static_cast<std::uint16_t>( 157 ipmi_flash::FirmwareFlags::UpdateFlags::openRead)); 158 159 std::fprintf(stderr, "Calling stat on %s session to check status\n", 160 versionBlob.c_str()); 161 162 /* TODO: call readBytes multiple times in case IPMI message length 163 * exceeds IPMI_MAX_MSG_LENGTH. 164 */ 165 auto size = pollReadReady(*session, blob); 166 if (size > 0) 167 { 168 return blob->readBytes(*session, 0, size); 169 } 170 return {}; 171 } 172 catch (const ipmiblob::BlobException& b) 173 { 174 throw ToolException("blob exception received: " + 175 std::string(b.what())); 176 } 177 } 178 179 void UpdateHandler::cleanArtifacts() 180 { 181 /* Errors aren't important for this call. */ 182 try 183 { 184 std::fprintf(stderr, "Executing cleanup blob\n"); 185 auto session = 186 openBlob(blob, ipmi_flash::cleanupBlobId, 187 static_cast<std::uint16_t>( 188 ipmi_flash::FirmwareFlags::UpdateFlags::openWrite)); 189 blob->commit(*session, {}); 190 } 191 catch (const std::exception& e) 192 { 193 std::fprintf(stderr, "Cleanup failed: %s\n", e.what()); 194 } 195 } 196 197 } // namespace host_tool 198