mapper: Add missing parent paths in ifaces-added

On an InterfacesAdded signal, a new path may have just been
created with 2 or more new path segments. For example /A/B/C/D
was just created and only /A/B exists so far.  In this case, the
current code never adds /A/B/C to the mapper, so trying to call
GetObject or GetSubTree on that path would fail.

To fix that, in the signal handler, check if all of the
parent paths are already in the interface map, and add
them if they aren't.  It will take a shortcut and just add
the three org.freedesktop.DBus interfaces Introspectable, Peer,
and Properties, instead of calling the D-Bus introspect method
since that is all it would find anyway.

A big usecase of this is the REST server, where people try to
do things like GET /xyz/openbmc_project/logging/entry/enumerate,
which will fail if no logs existed when the mapper started since
nothing explicitly created that exact path.

Resolves openbmc/openbmc#3450

Tested:  Created /xyz/openbmc_project/logging/entry/1,
when previously only /xyz/openbmc_project/logging existed.
Verify calling GetObject and GetSubTree now work on the
/xyz/openbmc_project/logging/entry path.

Change-Id: Id24f78beef9a34245d88de25a65006745fbda992
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/src/main.cpp b/src/main.cpp
index bcf52e1..b1325e1 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -678,6 +678,52 @@
                         addAssociation(server, associations, obj_path.str);
                     }
                 }
+
+                // To handle the case where an object path is being created
+                // with 2 or more new path segments, check if the parent paths
+                // of this path are already in the interface map, and add them
+                // if they aren't with just the default freedesktop interfaces.
+                // This would be done via introspection if they would have
+                // already existed at startup.  While we could also introspect
+                // them now to do the work, we know there aren't any other
+                // interfaces or we would have gotten signals for them as well,
+                // so take a shortcut to speed things up.
+                //
+                // This is all needed so that mapper operations can be done
+                // on the new parent paths.
+                using iface_map_iterator = interface_map_type::iterator;
+                using iface_map_value_type = boost::container::flat_map<
+                    std::string, boost::container::flat_set<std::string>>;
+                using name_map_iterator = iface_map_value_type::iterator;
+
+                static const boost::container::flat_set<std::string>
+                    default_ifaces{"org.freedesktop.DBus.Introspectable",
+                                   "org.freedesktop.DBus.Peer",
+                                   "org.freedesktop.DBus.Properties"};
+
+                std::string parent = obj_path.str;
+                auto pos = parent.find_last_of('/');
+
+                while (pos != std::string::npos)
+                {
+                    parent = parent.substr(0, pos);
+
+                    std::pair<iface_map_iterator, bool> parentEntry =
+                        interface_map.insert(
+                            std::make_pair(parent, iface_map_value_type{}));
+
+                    std::pair<name_map_iterator, bool> ifaceEntry =
+                        parentEntry.first->second.insert(
+                            std::make_pair(well_known, default_ifaces));
+
+                    if (!ifaceEntry.second)
+                    {
+                        // Entry was already there for this name so done.
+                        break;
+                    }
+
+                    pos = parent.find_last_of('/');
+                }
             }
         };