use start update D-Bus interface for usb manager

Use the start update D-Bus interface for usb manager. This change is
based on -
https://gerrit.openbmc.org/c/openbmc/phosphor-dbus-interfaces/+/65738
https://gerrit.openbmc.org/c/openbmc/docs/+/65739

Tested:
```
<6> StartUpdate succeeded, objectPath: /xyz/openbmc_project/software/ce236cf7
```

Change-Id: Ie8337836537d5ca5aca4bcaa0aa86cbd018752eb
Signed-off-by: Jagpal Singh Gill <paligill@gmail.com>
diff --git a/usb/usb_manager.cpp b/usb/usb_manager.cpp
index 226a578..89b0e9b 100644
--- a/usb/usb_manager.cpp
+++ b/usb/usb_manager.cpp
@@ -4,6 +4,10 @@
 
 #include <sys/mount.h>
 
+#include <xyz/openbmc_project/ObjectMapper/client.hpp>
+#include <xyz/openbmc_project/Software/ApplyTime/common.hpp>
+#include <xyz/openbmc_project/Software/Update/client.hpp>
+
 #include <system_error>
 
 namespace phosphor
@@ -11,7 +15,10 @@
 namespace usb
 {
 
-bool USBManager::run()
+using Association = std::tuple<std::string, std::string, std::string>;
+using Paths = std::vector<std::string>;
+
+bool USBManager::copyImage()
 {
     std::error_code ec;
     fs::path dir(usbPath);
@@ -41,6 +48,7 @@
 
             try
             {
+                imageDstPath = dstPath;
                 return fs::copy_file(fs::absolute(p.path()), dstPath);
             }
             catch (const std::exception& e)
@@ -56,6 +64,81 @@
     return false;
 }
 
+#ifdef START_UPDATE_DBUS_INTEFACE
+
+// NOLINTNEXTLINE(readability-static-accessed-through-instance)
+auto findAssociatedUpdatablePath(sdbusplus::async::context& ctx)
+    -> sdbusplus::async::task<Paths>
+{
+    constexpr auto associatedPath =
+        "/xyz/openbmc_project/software/bmc/updateable";
+    constexpr auto interface = "xyz.openbmc_project.Association";
+    constexpr auto propertyName = "endpoints";
+
+    co_return utils::getProperty<Paths>(ctx.get_bus(), associatedPath,
+                                        interface, propertyName);
+}
+
+// NOLINTNEXTLINE(readability-static-accessed-through-instance)
+auto USBManager::startUpdate(int fd) -> sdbusplus::async::task<bool>
+{
+    using Updater = sdbusplus::client::xyz::openbmc_project::software::Update<>;
+    using ApplyTimeIntf =
+        sdbusplus::common::xyz::openbmc_project::software::ApplyTime;
+
+    constexpr auto serviceName = "xyz.openbmc_project.Software.Manager";
+
+    auto paths = co_await findAssociatedUpdatablePath(ctx);
+    if (paths.size() != 1)
+    {
+        lg2::error("Failed to find associated updatable path");
+        co_return false;
+    }
+
+    auto updater = Updater(ctx).service(serviceName).path(paths[0]);
+    sdbusplus::message::object_path objectPath = co_await updater.start_update(
+        fd, ApplyTimeIntf::RequestedApplyTimes::OnReset);
+    if (objectPath.str.empty())
+    {
+        lg2::error("StartUpdate failed");
+        co_return false;
+    }
+    lg2::info("StartUpdate succeeded, objectPath: {PATH}", "PATH", objectPath);
+
+    co_return true;
+}
+
+// NOLINTNEXTLINE(readability-static-accessed-through-instance)
+auto USBManager::run() -> sdbusplus::async::task<void>
+{
+    auto res = copyImage();
+    if (!res)
+    {
+        lg2::error("Failed to copy image from USB");
+        co_return;
+    }
+
+    int fd = open(imageDstPath.c_str(), O_RDONLY);
+    if (fd < 0)
+    {
+        lg2::error("Failed to open {PATH}", "PATH", imageDstPath);
+        co_return;
+    }
+
+    co_await startUpdate(fd);
+
+    ctx.request_stop();
+
+    co_return;
+}
+
+#else
+
+bool USBManager::run()
+{
+    return copyImage();
+}
+
 void USBManager::setApplyTime()
 {
     utils::PropertyValue value =
@@ -130,5 +213,7 @@
     }
 }
 
+#endif // START_UPDATE_DBUS_INTEFACE
+
 } // namespace usb
 } // namespace phosphor
diff --git a/usb/usb_manager.hpp b/usb/usb_manager.hpp
index 98d6796..c7f6403 100644
--- a/usb/usb_manager.hpp
+++ b/usb/usb_manager.hpp
@@ -3,6 +3,7 @@
 #include "utils.hpp"
 
 #include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/async.hpp>
 #include <sdeventplus/event.hpp>
 
 #include <filesystem>
@@ -24,15 +25,38 @@
     USBManager& operator=(const USBManager&) = delete;
     USBManager& operator=(USBManager&&) = delete;
 
+#ifdef START_UPDATE_DBUS_INTEFACE
+
+    explicit USBManager(sdbusplus::async::context& ctx, const fs::path& devPath,
+                        const fs::path& usbPath) :
+        ctx(ctx), devicePath(devPath), usbPath(usbPath)
+    {
+        ctx.spawn(run());
+    }
+
+    /** @brief Run the USBManager */
+    auto run() -> sdbusplus::async::task<void>;
+
+  private:
+    /** @brief D-Bus context. */
+    sdbusplus::async::context& ctx;
+
+    /** @brief Starts the firmware update.
+     *  @param[in]  fd  - The file descriptor of the image to update.
+     *  @return Success or Fail
+     */
+    auto startUpdate(int fd) -> sdbusplus::async::task<bool>;
+
+#else
     explicit USBManager(sdbusplus::bus_t& bus, sdeventplus::Event& event,
                         const fs::path& devPath, const fs::path& usbPath) :
-        bus(bus), event(event), devicePath(devPath), usbPath(usbPath),
-        isUSBCodeUpdate(false),
+        bus(bus), event(event), isUSBCodeUpdate(false),
         fwUpdateMatcher(bus,
                         MatchRules::interfacesAdded() +
                             MatchRules::path("/xyz/openbmc_project/software"),
                         std::bind(std::mem_fn(&USBManager::updateActivation),
-                                  this, std::placeholders::_1))
+                                  this, std::placeholders::_1)),
+        devicePath(devPath), usbPath(usbPath)
     {
         if (!run())
         {
@@ -51,6 +75,19 @@
      */
     bool run();
 
+  private:
+    /** @brief Persistent sdbusplus DBus bus connection. */
+    sdbusplus::bus_t& bus;
+
+    /** sd event handler. */
+    sdeventplus::Event& event;
+
+    /** Indicates whether USB codeupdate is going on. */
+    bool isUSBCodeUpdate;
+
+    /** sdbusplus signal match for new image. */
+    sdbusplus::bus::match_t fwUpdateMatcher;
+
     /** @brief Creates an Activation D-Bus object.
      *
      * @param[in]  msg   - Data associated with subscribed signal
@@ -68,12 +105,14 @@
      */
     void setRequestedActivation(const std::string& path);
 
-  private:
-    /** @brief Persistent sdbusplus DBus bus connection. */
-    sdbusplus::bus_t& bus;
+#endif /* START_UPDATE_DBUS_INTEFACE */
 
-    /** sd event handler. */
-    sdeventplus::Event& event;
+    /** @brief Find the first file with a .tar extension according to the USB
+     *         file path and copy to IMG_UPLOAD_DIR
+     *
+     *  @return Success or Fail
+     */
+    bool copyImage();
 
     /** The USB device path. */
     const fs::path& devicePath;
@@ -81,11 +120,8 @@
     /** The USB mount path. */
     const fs::path& usbPath;
 
-    /** Indicates whether USB codeupdate is going on. */
-    bool isUSBCodeUpdate;
-
-    /** sdbusplus signal match for new image. */
-    sdbusplus::bus::match_t fwUpdateMatcher;
+    /** The destination path for copied over image file */
+    fs::path imageDstPath;
 };
 
 } // namespace usb
diff --git a/usb/usb_manager_main.cpp b/usb/usb_manager_main.cpp
index 771b4cd..3f0e5f5 100644
--- a/usb/usb_manager_main.cpp
+++ b/usb/usb_manager_main.cpp
@@ -7,11 +7,6 @@
 int main(int argc, char** argv)
 {
     namespace fs = std::filesystem;
-    // Dbus constructs
-    auto bus = sdbusplus::bus::new_default();
-
-    // Get a default event loop
-    auto event = sdeventplus::Event::get_default();
 
     std::string deviceName{};
 
@@ -21,6 +16,7 @@
 
     CLI11_PARSE(app, argc, argv);
 
+    // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
     if (deviceName.empty())
     {
         lg2::error("The file name passed in is empty.");
@@ -29,11 +25,28 @@
 
     fs::path devicePath = fs::path{"/dev"} / deviceName;
     fs::path usbPath = fs::path{"/run/media/usb"} / deviceName;
+
+#ifdef START_UPDATE_DBUS_INTEFACE
+
+    sdbusplus::async::context ctx;
+    phosphor::usb::USBManager manager(ctx, devicePath, usbPath);
+    ctx.run();
+
+#else
+
+    // Dbus constructs
+    auto bus = sdbusplus::bus::new_default();
+
+    // Get a default event loop
+    auto event = sdeventplus::Event::get_default();
+
     phosphor::usb::USBManager manager(bus, event, devicePath, usbPath);
 
     // Attach the bus to sd_event to service user requests
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
     event.loop();
 
+#endif // START_UPDATE_DBUS_INTEFACE
+
     return 0;
 }