monitor: support systemd delay getting on dbus

Testing has shown that systemd does not always get registered on dbus
before the phosphor-systemd-target-monitor application starts up and
calls into systemd dbus interfaces. The code in this commit ensures the
application handles both the scenario where it is available on startup
and the scenario where the interfaces becomes available after startup.

Tested:
Verified application started and subscribed to systemd signals in both
scenarios described above.

Resolves openbmc/phosphor-state-manager#10
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
Change-Id: I6594466681cd66f90ae27a6e935085a17120457a
diff --git a/systemd_target_signal.cpp b/systemd_target_signal.cpp
index 009a9d0..0f710b2 100644
--- a/systemd_target_signal.cpp
+++ b/systemd_target_signal.cpp
@@ -86,6 +86,24 @@
     return;
 }
 
+void SystemdTargetLogging::processNameChangeSignal(
+    sdbusplus::message::message& msg)
+{
+    std::string name;      // well-known
+    std::string old_owner; // unique-name
+    std::string new_owner; // unique-name
+
+    msg.read(name, old_owner, new_owner);
+
+    // Looking for systemd to be on dbus so we can call it
+    if (name == "org.freedesktop.systemd1")
+    {
+        log<level::INFO>("org.freedesktop.systemd1 is now on dbus");
+        subscribeToSystemdSignals();
+    }
+    return;
+}
+
 void SystemdTargetLogging::subscribeToSystemdSignals()
 {
     auto method = this->bus.new_method_call(
@@ -98,11 +116,27 @@
     }
     catch (const sdbusplus::exception::SdBusError& e)
     {
-        log<level::ERR>("Failed to subscribe to systemd signals",
-                        entry("SDBUSERR=%s", e.what()));
-        elog<InternalFailure>();
+        // If error indicates systemd is not on dbus yet then do nothing.
+        // The systemdNameChangeSignals callback will detect when it is on
+        // dbus and then call this function again
+        const std::string noDbus("org.freedesktop.DBus.Error.ServiceUnknown");
+        if (noDbus == e.name())
+        {
+            log<level::INFO>("org.freedesktop.systemd1 not on dbus yet");
+        }
+        else
+        {
+            log<level::ERR>("Failed to subscribe to systemd signals",
+                            entry("SDBUSERR=%s", e.what()));
+            elog<InternalFailure>();
+        }
+        return;
     }
 
+    // Call destructor on match callback since application is now subscribed to
+    // systemd signals
+    this->systemdNameOwnedChangedSignal.~match();
+
     return;
 }
 
diff --git a/systemd_target_signal.hpp b/systemd_target_signal.hpp
index d0719a8..3864602 100644
--- a/systemd_target_signal.hpp
+++ b/systemd_target_signal.hpp
@@ -39,7 +39,12 @@
                 sdbusplus::bus::match::rules::interface(
                     "org.freedesktop.systemd1.Manager"),
             std::bind(std::mem_fn(&SystemdTargetLogging::systemdUnitChange),
-                      this, std::placeholders::_1))
+                      this, std::placeholders::_1)),
+        systemdNameOwnedChangedSignal(
+            bus, sdbusplus::bus::match::rules::nameOwnerChanged(),
+            std::bind(
+                std::mem_fn(&SystemdTargetLogging::processNameChangeSignal),
+                this, std::placeholders::_1))
     {
     }
 
@@ -82,6 +87,16 @@
      */
     void systemdUnitChange(sdbusplus::message::message& msg);
 
+    /** @brief Wait for systemd to show up on dbus
+     *
+     * Once systemd is on dbus, this application can subscribe to systemd
+     * signal changes
+     *
+     * @param[in]  msg       - Data associated with subscribed signal
+     *
+     */
+    void processNameChangeSignal(sdbusplus::message::message& msg);
+
     /** @brief Systemd targets to monitor and error logs to create */
     const TargetErrorData& targetData;
 
@@ -90,6 +105,9 @@
 
     /** @brief Used to subscribe to dbus systemd JobRemoved signals **/
     sdbusplus::bus::match_t systemdJobRemovedSignal;
+
+    /** @brief Used to know when systemd has registered on dbus **/
+    sdbusplus::bus::match_t systemdNameOwnedChangedSignal;
 };
 
 } // namespace manager