Update Service: Change error message based on error logs

THis adds support for better error responses based on
the logs generated by phosphor-software-manager.

Tested: Got 400 error with different messages based
on failure type

{
    "error": {
        "@Message.ExtendedInfo": [
            {
                "@odata.type": "/redfish/v1/$metadata#Message.v1_0_0.Message",
                "Message": "Invalid file uploaded to /redfish/v1/UpdateService: Invalid archive.",
                "MessageArgs": [
                    "/redfish/v1/UpdateService",
                    "invalid archive"
                ],
                "MessageId": "OpenBMC.0.1.0.InvalidUpload",
                "Resolution": "None.",
                "Severity": "Warning"
            }
        ],
        "code": "OpenBMC.0.1.0.InvalidUpload",
        "message": "Invalid file uploaded to /redfish/v1/UpdateService: Invalid archive."
    }
}

{
    "error": {
        "@Message.ExtendedInfo": [
            {
                "@odata.type": "/redfish/v1/$metadata#Message.v1_0_0.Message",
                "Message": "Invalid file uploaded to /redfish/v1/UpdateService: Invalid image format.",
                "MessageArgs": [
                    "/redfish/v1/UpdateService",
                    "invalid image format"
                ],
                "MessageId": "OpenBMC.0.1.0.InvalidUpload",
                "Resolution": "None.",
                "Severity": "Warning"
            }
        ],
        "code": "OpenBMC.0.1.0.InvalidUpload",
        "message": "Invalid file uploaded to /redfish/v1/UpdateService: Invalid image format."
    }
}

{
    "error": {
        "@Message.ExtendedInfo": [
            {
                "@odata.type": "#Message.v1_0_0.Message",
                "Message": "The resource /redfish/v1/UpdateService was unable to satisfy the request due to unavailability of resources.",
                "MessageArgs": [
                    "/redfish/v1/UpdateService"
                ],
                "MessageId": "Base.1.4.0.ResourceExhaustion",
                "Resolution": "Ensure that the resources are available and resubmit the request.",
                "Severity": "Critical"
            }
        ],
        "code": "Base.1.4.0.ResourceExhaustion",
        "message": "The resource /redfish/v1/UpdateService was unable to satisfy the request due to unavailability of resources."
    }
}

Change-Id: Ida9a23c10aedbf9a48c96f4050a04e06bddff284
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/redfish-core/include/error_messages.hpp b/redfish-core/include/error_messages.hpp
index ca921e9..0243be9 100644
--- a/redfish-core/include/error_messages.hpp
+++ b/redfish-core/include/error_messages.hpp
@@ -775,6 +775,18 @@
  * @returns Message PasswordChangeRequired formatted to JSON */
 void passwordChangeRequired(crow::Response& res, const std::string& arg1);
 
+/**
+ * @brief Formats InvalidUpload message into JSON
+ * Message body: Invalid file uploaded to %1: %2.*
+ * @param[in] arg1 Parameter of message that will replace %1 in its body.
+ * @param[in] arg2 Parameter of message that will replace %2 in its body.
+ *
+ * @returns Message InvalidUpload formatted to JSON */
+nlohmann::json invalidUpload(const std::string& arg1, const std::string& arg2);
+
+void invalidUpload(crow::Response& res, const std::string& arg1,
+                   const std::string& arg2);
+
 } // namespace messages
 
 } // namespace redfish
diff --git a/redfish-core/include/registries/openbmc_message_registry.hpp b/redfish-core/include/registries/openbmc_message_registry.hpp
index 0856370..17b2070 100644
--- a/redfish-core/include/registries/openbmc_message_registry.hpp
+++ b/redfish-core/include/registries/openbmc_message_registry.hpp
@@ -29,7 +29,7 @@
     "0.1.0",
     "OpenBMC",
 };
-constexpr std::array<MessageEntry, 181> registry = {
+constexpr std::array<MessageEntry, 182> registry = {
     MessageEntry{
         "ADDDCCorrectable",
         {
@@ -453,6 +453,15 @@
                      {"string"},
                      "None.",
                  }},
+    MessageEntry{"InvalidUpload",
+                 {
+                     "Indicates that the uploaded file was invalid.",
+                     "Invalid file uploaded to %1: %2.",
+                     "Warning",
+                     2,
+                     {"string", "string"},
+                     "None.",
+                 }},
     MessageEntry{
         "InventoryAdded",
         {
diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp
index fcb880e..8dae2fb 100644
--- a/redfish-core/lib/update_service.hpp
+++ b/redfish-core/lib/update_service.hpp
@@ -27,6 +27,7 @@
 
 // Match signals added on software path
 static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
+static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateErrorMatcher;
 // Only allow one update at a time
 static bool fwUpdateInProgress = false;
 // Timer for software available
@@ -36,6 +37,7 @@
 {
     fwUpdateInProgress = false;
     fwUpdateMatcher = nullptr;
+    fwUpdateErrorMatcher = nullptr;
 }
 static void activateImage(const std::string& objPath,
                           const std::string& service)
@@ -256,6 +258,7 @@
 // then no asyncResp updates will occur
 static void monitorForSoftwareAvailable(std::shared_ptr<AsyncResp> asyncResp,
                                         const crow::Request& req,
+                                        const std::string& url,
                                         int timeoutTimeSeconds = 5)
 {
     // Only allow one FW update at a time
@@ -308,6 +311,62 @@
         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
         callback);
+
+    fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match::match>(
+        *crow::connections::systemBus,
+        "type='signal',member='PropertiesChanged',path_namespace='/xyz/"
+        "openbmc_project/logging/entry',"
+        "arg0='xyz.openbmc_project.Logging.Entry'",
+        [asyncResp, url](sdbusplus::message::message& m) {
+            BMCWEB_LOG_DEBUG << "Error Match fired";
+            boost::container::flat_map<std::string, std::variant<std::string>>
+                values;
+            std::string objName;
+            m.read(objName, values);
+            auto find = values.find("Message");
+            if (find == values.end())
+            {
+                return;
+            }
+            std::string* type = std::get_if<std::string>(&(find->second));
+            if (type == nullptr)
+            {
+                return; // if this was our message, timeout will cover it
+            }
+            if (!boost::starts_with(*type,
+                                    "xyz.openbmc_project.Software.Image.Error"))
+            {
+                return;
+            }
+            if (*type ==
+                "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
+            {
+                redfish::messages::invalidUpload(asyncResp->res, url,
+                                                 "Invalid archive");
+            }
+            else if (*type == "xyz.openbmc_project.Software.Image.Error."
+                              "ManifestFileFailure")
+            {
+                redfish::messages::invalidUpload(asyncResp->res, url,
+                                                 "Invalid manifest");
+            }
+            else if (*type ==
+                     "xyz.openbmc_project.Software.Image.Error.ImageFailure")
+            {
+                redfish::messages::invalidUpload(asyncResp->res, url,
+                                                 "Invalid image format");
+            }
+            else if (*type ==
+                     "xyz.openbmc_project.Software.Image.Error.BusyFailure")
+            {
+                redfish::messages::resourceExhaustion(asyncResp->res, url);
+            }
+            else
+            {
+                redfish::messages::internalError(asyncResp->res);
+            }
+            fwAvailableTimer = nullptr;
+        });
 }
 
 /**
@@ -410,7 +469,10 @@
 
         // Setup callback for when new software detected
         // Give TFTP 2 minutes to complete
-        monitorForSoftwareAvailable(nullptr, req, 120);
+        monitorForSoftwareAvailable(
+            nullptr, req,
+            "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
+            120);
 
         // TFTP can take up to 2 minutes depending on image size and
         // connection speed. Return to caller as soon as the TFTP operation
@@ -605,7 +667,8 @@
         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
 
         // Setup callback for when new software detected
-        monitorForSoftwareAvailable(asyncResp, req);
+        monitorForSoftwareAvailable(asyncResp, req,
+                                    "/redfish/v1/UpdateService");
 
         std::string filepath(
             "/tmp/images/" +
diff --git a/redfish-core/src/error_messages.cpp b/redfish-core/src/error_messages.cpp
index 4be2687..160b73f 100644
--- a/redfish-core/src/error_messages.cpp
+++ b/redfish-core/src/error_messages.cpp
@@ -1725,6 +1725,31 @@
                            "provided."}});
 }
 
+void invalidUpload(crow::Response& res, const std::string& arg1,
+                   const std::string& arg2)
+{
+    res.result(boost::beast::http::status::bad_request);
+    addMessageToErrorJson(res.jsonValue, invalidUpload(arg1, arg2));
+}
+
+/**
+ * @internal
+ * @brief Formats Invalid File message into JSON
+ *
+ * See header file for more information
+ * @endinternal
+ */
+nlohmann::json invalidUpload(const std::string& arg1, const std::string& arg2)
+{
+    return nlohmann::json{
+        {"@odata.type", "/redfish/v1/$metadata#Message.v1_0_0.Message"},
+        {"MessageId", "OpenBMC.0.1.0.InvalidUpload"},
+        {"Message", "Invalid file uploaded to " + arg1 + ": " + arg2 + "."},
+        {"MessageArgs", {arg1, arg2}},
+        {"Severity", "Warning"},
+        {"Resolution", "None."}};
+}
+
 } // namespace messages
 
 } // namespace redfish