tools: blob: add exceptions

Add exceptions on failures where it's a clean failure path.

Change-Id: Iaa8b6c7a0914367866092a7e31899453183fd7b2
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/test/tools_updater_unittest.cpp b/test/tools_updater_unittest.cpp
index d36304b..bc7417c 100644
--- a/test/tools_updater_unittest.cpp
+++ b/test/tools_updater_unittest.cpp
@@ -17,8 +17,9 @@
     BlobInterfaceMock blobMock;
     std::string firmwareImage = "image.bin";
     std::string signatureFile = "image.sig";
+    std::string expectedBlob = "/flash/image";
 
-    std::vector<std::string> blobList = {"/flash/image"};
+    std::vector<std::string> blobList = {expectedBlob};
     StatResponse statObj;
     statObj.blob_state = blobs::FirmwareBlobHandler::UpdateFlags::ipmi |
                          blobs::FirmwareBlobHandler::UpdateFlags::lpc;
@@ -29,12 +30,11 @@
 
     EXPECT_CALL(blobMock, getBlobList()).WillOnce(Return(blobList));
 
-    EXPECT_CALL(blobMock, getStat(StrEq(blobList[0].c_str())))
-        .WillOnce(Return(statObj));
+    EXPECT_CALL(blobMock, getStat(Eq(expectedBlob))).WillOnce(Return(statObj));
 
     EXPECT_CALL(handlerMock, supportedType()).WillOnce(Return(supported));
 
-    EXPECT_CALL(blobMock, openBlob(StrEq(blobList[0].c_str()), Eq(supported)))
+    EXPECT_CALL(blobMock, openBlob(StrEq(expectedBlob.c_str()), Eq(supported)))
         .WillOnce(Return(session));
 
     EXPECT_CALL(handlerMock,
diff --git a/tools/blob_handler.cpp b/tools/blob_handler.cpp
index 43fc891..62bbb4d 100644
--- a/tools/blob_handler.cpp
+++ b/tools/blob_handler.cpp
@@ -56,8 +56,7 @@
     }
     catch (const IpmiException& e)
     {
-        std::fprintf(stderr, "Received exception: %s\n", e.what());
-        return {};
+        throw BlobException(e.what());
     }
 
     /* IPMI_CC was OK, and it returned no bytes, so let's be happy with that for
@@ -75,16 +74,14 @@
      */
     if (reply.size() < headerSize)
     {
-        std::fprintf(stderr, "Invalid response length\n");
-        return {};
+        throw BlobException("Invalid response length");
     }
 
     /* Verify the OEN. */
     if (std::memcmp(ipmiPhosphorOen.data(), reply.data(),
                     ipmiPhosphorOen.size()) != 0)
     {
-        std::fprintf(stderr, "Invalid OEN received\n");
-        return {};
+        throw BlobException("Invalid OEN received");
     }
 
     /* Validate CRC. */
@@ -106,7 +103,7 @@
     {
         std::fprintf(stderr, "Invalid CRC, received: 0x%x, computed: 0x%x\n",
                      crc, computed);
-        return {};
+        throw BlobException("Invalid CRC on received data.");
     }
 
     return bytes;
@@ -115,14 +112,23 @@
 int BlobHandler::getBlobCount()
 {
     std::uint32_t count;
-    auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobGetCount, {});
-    if (resp.size() != sizeof(count))
+    try
+    {
+        auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobGetCount, {});
+        if (resp.size() != sizeof(count))
+        {
+            return 0;
+        }
+
+        /* LE to LE (need to make this portable as some point. */
+        std::memcpy(&count, resp.data(), sizeof(count));
+    }
+    catch (const BlobException& b)
     {
         return 0;
     }
 
-    /* LE to LE (need to make this portable as some point. */
-    std::memcpy(&count, resp.data(), sizeof(count));
+    std::fprintf(stderr, "BLOB Count: %d\n", count);
     return count;
 }
 
@@ -130,11 +136,19 @@
 {
     std::vector<std::uint8_t> payload;
     std::uint8_t* data = reinterpret_cast<std::uint8_t*>(&index);
+
     std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
 
-    auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobEnumerate, payload);
-    return (resp.size() > 0) ? std::string(&resp[0], &resp[resp.size() - 1])
-                             : "";
+    try
+    {
+        auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobEnumerate, payload);
+        return (resp.size() > 0) ? std::string(&resp[0], &resp[resp.size() - 1])
+                                 : "";
+    }
+    catch (const BlobException& b)
+    {
+        return "";
+    }
 }
 
 std::vector<std::string> BlobHandler::getBlobList()
@@ -158,11 +172,20 @@
 StatResponse BlobHandler::getStat(const std::string& id)
 {
     StatResponse meta;
-    std::vector<std::uint8_t> name;
+    std::vector<std::uint8_t> name, resp;
+
     std::copy(id.begin(), id.end(), std::back_inserter(name));
     name.push_back(0x00); /* need to add nul-terminator. */
 
-    auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobStat, name);
+    try
+    {
+        resp = sendIpmiPayload(BlobOEMCommands::bmcBlobStat, name);
+    }
+    catch (const BlobException& b)
+    {
+        throw;
+    }
+
     std::memcpy(&meta.blob_state, &resp[0], sizeof(meta.blob_state));
     std::memcpy(&meta.size, &resp[sizeof(meta.blob_state)], sizeof(meta.size));
     int offset = sizeof(meta.blob_state) + sizeof(meta.size);
@@ -181,16 +204,25 @@
                           blobs::FirmwareBlobHandler::UpdateFlags handlerFlags)
 {
     std::uint16_t session;
-    std::vector<std::uint8_t> request;
+    std::vector<std::uint8_t> request, resp;
     std::uint16_t flags =
         blobs::FirmwareBlobHandler::UpdateFlags::openWrite | handlerFlags;
     auto addrFlags = reinterpret_cast<std::uint8_t*>(&flags);
+
     std::copy(addrFlags, addrFlags + sizeof(flags),
               std::back_inserter(request));
     std::copy(id.begin(), id.end(), std::back_inserter(request));
     request.push_back(0x00); /* need to add nul-terminator. */
 
-    auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobOpen, request);
+    try
+    {
+        resp = sendIpmiPayload(BlobOEMCommands::bmcBlobOpen, request);
+    }
+    catch (const BlobException& b)
+    {
+        throw;
+    }
+
     if (resp.size() != sizeof(session))
     {
         throw BlobException("Did not receive session.");
diff --git a/tools/blob_handler.hpp b/tools/blob_handler.hpp
index bcac0a8..73f0d94 100644
--- a/tools/blob_handler.hpp
+++ b/tools/blob_handler.hpp
@@ -30,6 +30,7 @@
      * @param[in] command - the blob command.
      * @param[in] payload - the payload bytes.
      * @return the bytes returned from the ipmi interface.
+     * @throws BlobException.
      */
     std::vector<std::uint8_t>
         sendIpmiPayload(BlobOEMCommands command,
@@ -51,7 +52,15 @@
     std::string enumerateBlob(std::uint32_t index);
 
     std::vector<std::string> getBlobList() override;
+
+    /**
+     * @throws BlobException.
+     */
     StatResponse getStat(const std::string& id) override;
+
+    /**
+     * @throws BlobException.
+     */
     std::uint16_t
         openBlob(const std::string& id,
                  blobs::FirmwareBlobHandler::UpdateFlags handlerFlags) override;
diff --git a/tools/updater.cpp b/tools/updater.cpp
index b9574d8..b7ea191 100644
--- a/tools/updater.cpp
+++ b/tools/updater.cpp
@@ -20,6 +20,7 @@
 #include "tool_errors.hpp"
 
 #include <algorithm>
+#include <cstring>
 #include <memory>
 
 void updaterMain(BlobInterface* blob, DataInterface* handler,
@@ -36,7 +37,15 @@
      * available and use it.
      */
     std::vector<std::string> blobs = blob->getBlobList();
-    auto blobInst = std::find(blobs.begin(), blobs.end(), goalFirmware);
+    auto blobInst = std::find_if(
+        blobs.begin(), blobs.end(), [&goalFirmware](const auto& iter) {
+            /* Running into weird scenarios where the string comparison doesn't
+             * work.  TODO: revisit.
+             */
+            return (0 == std::memcmp(goalFirmware.c_str(), iter.c_str(),
+                                     goalFirmware.length()));
+            // return (goalFirmware.compare(iter));
+        });
     if (blobInst == blobs.end())
     {
         throw ToolException(goalFirmware + " not found");
@@ -45,7 +54,17 @@
     /* Call stat on /flash/image (or /flash/tarball) and check if data interface
      * is supported.
      */
-    auto stat = blob->getStat(goalFirmware);
+    StatResponse stat;
+    try
+    {
+        stat = blob->getStat(goalFirmware);
+    }
+    catch (const BlobException& b)
+    {
+        throw ToolException("blob exception received: " +
+                            std::string(b.what()));
+    }
+
     auto supported = handler->supportedType();
     if ((stat.blob_state & supported) == 0)
     {
@@ -64,8 +83,6 @@
                             std::string(b.what()));
     }
 
-    std::fprintf(stderr, "using session: %d\n", session);
-
     /* Send over the firmware image. */
     if (!handler->sendContents(imagePath, session))
     {