regulators: Handle expected D-Bus exceptions

When hardware is not present, it can appear multiple ways on D-Bus:
* Object path does not exist
* Object path exists, but Inventory.Item interface not implemented
* Inventory.Item interface implemented with Present value of false

The first two cases result in a D-Bus exception being thrown when trying
to obtain the value of the Present property.

Enhance the DBusPresenceService class to catch these exceptions and
treat them as a false Presence value.

Tested:
* Tested where hardware present
* Tested where hardware not present
  * Object path and interface/property exist
  * Object path exists
  * Object path does not exist
* For full test plan, see
  https://gist.github.com/smccarney/d3d7384700abcc5abf436e2b859d98e5

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: I543a4c16984dea3657b8024dedd1060dda4319e2
diff --git a/phosphor-regulators/src/presence_service.cpp b/phosphor-regulators/src/presence_service.cpp
index 7233d28..9e15b03 100644
--- a/phosphor-regulators/src/presence_service.cpp
+++ b/phosphor-regulators/src/presence_service.cpp
@@ -24,6 +24,7 @@
 
 bool DBusPresenceService::isPresent(const std::string& inventoryPath)
 {
+    // Initially assume hardware is not present
     bool present{false};
 
     // Try to find cached presence value
@@ -35,8 +36,24 @@
     else
     {
         // Get presence from D-Bus interface/property
-        util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath,
-                          INVENTORY_MGR_IFACE, bus, present);
+        try
+        {
+            util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath,
+                              INVENTORY_MGR_IFACE, bus, present);
+        }
+        catch (const sdbusplus::exception::SdBusError& e)
+        {
+            // If exception type is expected and indicates hardware not present
+            if (isExpectedException(e))
+            {
+                present = false;
+            }
+            else
+            {
+                // Re-throw unexpected exception
+                throw;
+            }
+        }
 
         // Cache presence value
         cache[inventoryPath] = present;
@@ -45,4 +62,36 @@
     return present;
 }
 
+bool DBusPresenceService::isExpectedException(
+    const sdbusplus::exception::SdBusError& e)
+{
+    // Initially assume exception is not one of the expected types
+    bool isExpected{false};
+
+    // If the D-Bus error name is set within the exception
+    if (e.name() != nullptr)
+    {
+        // Check if the error name is one of the expected values when hardware
+        // is not present.
+        //
+        // Sometimes the object path does not exist.  Sometimes the object path
+        // exists, but it does not implement the D-Bus interface that contains
+        // the present property.  Both of these cases result in exceptions.
+        //
+        // In the case where the interface is not implemented, the systemd
+        // documentation seems to indicate that the error name should be
+        // SD_BUS_ERROR_UNKNOWN_INTERFACE.  However, in OpenBMC the
+        // SD_BUS_ERROR_UNKNOWN_PROPERTY error name can occur.
+        std::string name = e.name();
+        if ((name == SD_BUS_ERROR_UNKNOWN_OBJECT) ||
+            (name == SD_BUS_ERROR_UNKNOWN_INTERFACE) ||
+            (name == SD_BUS_ERROR_UNKNOWN_PROPERTY))
+        {
+            isExpected = true;
+        }
+    }
+
+    return isExpected;
+}
+
 } // namespace phosphor::power::regulators