update service: simplify object path lookup

Simplify the object path lookup for the update interface by placing it
at the same object path as the version interface. This involves moving
the update interface to /xyz/openbmc_project/software/<swId> rather than
/xyz/openbmc_project/software/<deviceX>.
This change is based on -
https://gerrit.openbmc.org/c/openbmc/phosphor-dbus-interfaces/+/65738
https://gerrit.openbmc.org/c/openbmc/docs/+/65739

Related Commit from phosphor-bmc-code-mgmt -
https://gerrit.openbmc.org/c/openbmc/phosphor-bmc-code-mgmt/+/70668

Tested -
FirmwareInventory as TargetURI:
```
> curl -k -H "X-Auth-Token: $token" -H "Content-Type:multipart/form-data" -X POST -F UpdateParameters="{\"Targets\":[\"/redfish/v1/UpdateService/FirmwareInventory/3c956be0\"],\"@Redfish.OperationApplyTime\":\"Immediate\"};type=application/json" -F "UpdateFile=@obmc-phosphor-image-romulus-20240529184214.static.mtd.tar;type=application/octet-stream" https://${bmc}/redfish/v1/UpdateService/update
{
  "@odata.id": "/redfish/v1/TaskService/Tasks/0",
  "@odata.type": "#Task.v1_4_3.Task",
  "Id": "0",
  "TaskState": "Running",
  "TaskStatus": "OK"
}
```

/redfish/v1/Managers/bmc as Target URI:
```
> curl -k -H "X-Auth-Token: $token" -H "Content-Type:multipart/form-data" -X POST -F UpdateParameters="{\"Targets\":[\"/redfish/v1/Managers/bmc\"],\"@Redfish.OperationApplyTime\":\"Immediate\"};type=application/json" -F "UpdateFile=@obmc-phosphor-image-romulus-20240529184214.static.mtd.tar;type=application/octet-stream" https://${bmc}/redfish/v1/UpdateService/update
{
  "@odata.id": "/redfish/v1/TaskService/Tasks/0",
  "@odata.type": "#Task.v1_4_3.Task",
  "Id": "0",
  "TaskState": "Running",
  "TaskStatus": "OK"
}
```

Redfish service validator passing:
```
Elapsed time: 0:04:33
metadataNamespaces: 3727
pass: 5184
passAction: 16
passGet: 213
passRedfishUri: 205
skipNoSchema: 3
skipOptional: 3535
unvalidated: 1
warnDeprecated: 5
warningPresent: 6
```

Change-Id: I6c22a904cecaf8e3043706990ae3a71da8f5addf
Signed-off-by: Jagpal Singh Gill <paligill@gmail.com>
diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp
index 683a8cb..ade9e57 100644
--- a/redfish-core/lib/update_service.hpp
+++ b/redfish-core/lib/update_service.hpp
@@ -912,46 +912,14 @@
         "StartUpdate", sdbusplus::message::unix_fd(memfd.fd), applyTime);
 }
 
-inline void getAssociatedUpdateInterface(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
-    const MemoryFileDescriptor& memfd, const std::string& applyTime,
-    const boost::system::error_code& ec,
-    const dbus::utility::MapperGetSubTreeResponse& subtree)
+inline void getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                      task::Payload payload, const MemoryFileDescriptor& memfd,
+                      const std::string& applyTime, const std::string& target,
+                      const boost::system::error_code& ec,
+                      const dbus::utility::MapperGetSubTreeResponse& subtree)
 {
-    if (ec)
-    {
-        BMCWEB_LOG_ERROR("error_code = {}", ec);
-        BMCWEB_LOG_ERROR("error msg = {}", ec.message());
-        messages::internalError(asyncResp->res);
-        return;
-    }
-    BMCWEB_LOG_DEBUG("Found {} startUpdate subtree paths", subtree.size());
-
-    if (subtree.size() > 1)
-    {
-        BMCWEB_LOG_ERROR("Found more than one startUpdate subtree paths");
-        messages::internalError(asyncResp->res);
-        return;
-    }
-
-    auto objectPath = subtree[0].first;
-    auto serviceName = subtree[0].second[0].first;
-
-    BMCWEB_LOG_DEBUG("Found objectPath {} serviceName {}", objectPath,
-                     serviceName);
-    startUpdate(asyncResp, std::move(payload), memfd, applyTime, objectPath,
-                serviceName);
-}
-
-inline void
-    getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-              task::Payload payload, MemoryFileDescriptor memfd,
-              const std::string& applyTime, const std::string& target,
-              const boost::system::error_code& ec,
-              const dbus::utility::MapperGetSubTreePathsResponse& subtree)
-{
-    using SwInfoMap =
-        std::unordered_map<std::string, sdbusplus::message::object_path>;
+    using SwInfoMap = std::unordered_map<
+        std::string, std::pair<sdbusplus::message::object_path, std::string>>;
     SwInfoMap swInfoMap;
 
     if (ec)
@@ -963,11 +931,11 @@
     }
     BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size());
 
-    for (const auto& objectPath : subtree)
+    for (const auto& entry : subtree)
     {
-        sdbusplus::message::object_path path(objectPath);
+        sdbusplus::message::object_path path(entry.first);
         std::string swId = path.filename();
-        swInfoMap.emplace(swId, path);
+        swInfoMap.emplace(swId, make_pair(path, entry.second[0].first));
     }
 
     auto swEntry = swInfoMap.find(target);
@@ -978,23 +946,37 @@
         return;
     }
 
-    BMCWEB_LOG_DEBUG("Found software version path {}", swEntry->second.str);
+    BMCWEB_LOG_DEBUG("Found software version path {} serviceName {}",
+                     swEntry->second.first.str, swEntry->second.second);
 
-    sdbusplus::message::object_path swObjectPath = swEntry->second /
-                                                   "software_version";
-    constexpr std::array<std::string_view, 1> interfaces = {
-        "xyz.openbmc_project.Software.Update"};
-    dbus::utility::getAssociatedSubTree(
-        swObjectPath,
-        sdbusplus::message::object_path("/xyz/openbmc_project/software"), 0,
-        interfaces,
-        [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
-         applyTime](
-            const boost::system::error_code& ec1,
-            const dbus::utility::MapperGetSubTreeResponse& subtree1) mutable {
-        getAssociatedUpdateInterface(asyncResp, std::move(payload), memfd,
-                                     applyTime, ec1, subtree1);
-    });
+    startUpdate(asyncResp, std::move(payload), memfd, applyTime,
+                swEntry->second.first.str, swEntry->second.second);
+}
+
+inline void
+    handleBMCUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                    task::Payload payload, const MemoryFileDescriptor& memfd,
+                    const std::string& applyTime,
+                    const boost::system::error_code& ec,
+                    const dbus::utility::MapperEndPoints& functionalSoftware)
+{
+    if (ec)
+    {
+        BMCWEB_LOG_ERROR("error_code = {}", ec);
+        BMCWEB_LOG_ERROR("error msg = {}", ec.message());
+        messages::internalError(asyncResp->res);
+        return;
+    }
+    if (functionalSoftware.size() != 1)
+    {
+        BMCWEB_LOG_ERROR("Found {} functional software endpoints",
+                         functionalSoftware.size());
+        messages::internalError(asyncResp->res);
+        return;
+    }
+
+    startUpdate(asyncResp, std::move(payload), memfd, applyTime,
+                functionalSoftware[0], "xyz.openbmc_project.Software.Manager");
 }
 
 inline void
@@ -1025,23 +1007,28 @@
 
     if (!targets.empty() && targets[0] == BMCWEB_REDFISH_MANAGER_URI_NAME)
     {
-        startUpdate(asyncResp, std::move(payload), memfd, applyTime,
-                    "/xyz/openbmc_project/software/bmc",
-                    "xyz.openbmc_project.Software.Manager");
+        dbus::utility::getAssociationEndPoints(
+            "/xyz/openbmc_project/software/bmc/functional",
+            [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
+             applyTime](
+                const boost::system::error_code& ec,
+                const dbus::utility::MapperEndPoints& objectPaths) mutable {
+            handleBMCUpdate(asyncResp, std::move(payload), memfd, applyTime, ec,
+                            objectPaths);
+        });
     }
     else
     {
         constexpr std::array<std::string_view, 1> interfaces = {
             "xyz.openbmc_project.Software.Version"};
-        dbus::utility::getSubTreePaths(
+        dbus::utility::getSubTree(
             "/xyz/openbmc_project/software", 1, interfaces,
             [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
-             applyTime,
-             targets](const boost::system::error_code& ec,
-                      const dbus::utility::MapperGetSubTreePathsResponse&
-                          subtree) mutable {
-            getSwInfo(asyncResp, std::move(payload), std::move(memfd),
-                      applyTime, targets[0], ec, subtree);
+             applyTime, targets](const boost::system::error_code& ec,
+                                 const dbus::utility::MapperGetSubTreeResponse&
+                                     subtree) mutable {
+            getSwInfo(asyncResp, std::move(payload), memfd, applyTime,
+                      targets[0], ec, subtree);
         });
     }
 }