diff options
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | README.md | 23 | ||||
-rw-r--r-- | events.cpp | 99 | ||||
-rw-r--r-- | events.hpp | 132 | ||||
-rw-r--r-- | example/events.d/match2.yaml | 32 | ||||
-rw-r--r-- | test/test.cpp | 78 |
6 files changed, 365 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am index 36edbca..16d35b9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,6 +14,7 @@ noinst_LTLIBRARIES = libmanager.la libmanager_la_SOURCES = \ xyz.openbmc_project.Inventory.Manager.cpp \ generated.cpp \ + events.cpp \ manager.cpp libmanager_la_LIBADD = libextra.la @@ -47,15 +47,38 @@ The available filters provided by PIM are: * propertyChangedTo - Only match events when the specified property has the specified value. +* propertyIs - Only match events when the specified property has +the specified value. ---- **propertyChangedTo** +The property under test is obtained from an sdbus message +generated from an org.freedesktop.DBus.Properties.PropertiesChanged +signal payload. + Supported arguments for the propertyChangedTo filter are: * interface - The interface hosting the property to be checked. * property - The property to check. * value - The value to check. +---- +**propertyIs** + +The property under test is obtained by invoking +org.freedesktop.Properties.Get on the specified interface. + +Supported arguments for the propertyIs filter are: +* path - The object hosting the property to be checked. +* interface - The interface hosting the property to be checked. +* property - The property to check. +* value - The value to check. +* service - An optional DBus service name. + +The service argument is optional. If provided that service will +be called explicitly. If omitted, the service will be obtained +with an xyz.openbmc_project.ObjectMapper lookup. + --- **actions** diff --git a/events.cpp b/events.cpp new file mode 100644 index 0000000..41a6e5f --- /dev/null +++ b/events.cpp @@ -0,0 +1,99 @@ +/** + * Copyright © 2017 IBM Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "events.hpp" +#include <sdbusplus/bus.hpp> + +namespace phosphor +{ +namespace inventory +{ +namespace manager +{ +namespace filters +{ +namespace details +{ +namespace property_condition +{ + +bool PropertyConditionBase::operator()( + sdbusplus::bus::bus& bus, + sdbusplus::message::message&, + Manager&) const +{ + std::string host; + + if (_service) + { + host.assign(_service); + } + else + { + auto mapperCall = bus.new_method_call( + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/ObjectMapper", + "xyz.openbmc_project.ObjectMapper", + "GetObject"); + mapperCall.append(_path); + mapperCall.append(std::vector<std::string>({_iface})); + + auto mapperResponseMsg = bus.call(mapperCall); + if (mapperResponseMsg.is_method_error()) + { + return false; + } + + std::map<std::string, std::vector<std::string>> mapperResponse; + mapperResponseMsg.read(mapperResponse); + + if (mapperResponse.begin() == mapperResponse.end()) + { + return false; + } + + host = mapperResponse.begin()->first; + + if (host == bus.get_unique_name()) + { + // TODO I can't call myself here. + return false; + } + } + auto hostCall = bus.new_method_call( + host.c_str(), + _path.c_str(), + "org.freedesktop.DBus.Properties", + "Get"); + hostCall.append(_iface); + hostCall.append(_property); + + auto hostResponseMsg = bus.call(hostCall); + if (hostResponseMsg.is_method_error()) + { + return false; + } + + return eval(hostResponseMsg); +} + +} // namespace property_condition +} // namespace details +} // namespace filters +} // namespace manager +} // namespace inventory +} // namespace phosphor + +// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 @@ -154,6 +154,120 @@ struct PropertyChangedCondition U _condition; }; +/** @struct PropertyConditionBase + * @brief Match filter functor that tests a property value. + * + * Base class for PropertyCondition - factored out code that + * doesn't need to be templated. + */ +struct PropertyConditionBase +{ + PropertyConditionBase() = delete; + virtual ~PropertyConditionBase() = default; + PropertyConditionBase(const PropertyConditionBase&) = delete; + PropertyConditionBase& operator=(const PropertyConditionBase&) = delete; + PropertyConditionBase(PropertyConditionBase&&) = default; + PropertyConditionBase& operator=(PropertyConditionBase&&) = default; + + /** @brief Constructor + * + * The service argument can be nullptr. If something + * else is provided the function will call the the + * service directly. If omitted, the function will + * look up the service in the ObjectMapper. + * + * @param path - The path of the object containing + * the property to be tested. + * @param iface - The interface hosting the property + * to be tested. + * @param property - The property to be tested. + * @param service - The DBus service hosting the object. + */ + PropertyConditionBase( + const char* path, + const char* iface, + const char* property, + const char* service) : + _path(path), + _iface(iface), + _property(property), + _service(service) {} + + /** @brief Forward comparison to type specific implementation. */ + virtual bool eval(sdbusplus::message::message&) const = 0; + + /** @brief Test a property value. + * + * Make a DBus call and test the value of any property. + */ + bool operator()( + sdbusplus::bus::bus&, + sdbusplus::message::message&, + Manager&) const; + + private: + std::string _path; + std::string _iface; + std::string _property; + const char* _service; +}; + +/** @struct PropertyCondition + * @brief Match filter functor that tests a property value. + * + * @tparam T - The type of the property being tested. + * @tparam U - The type of the condition checking functor. + */ +template <typename T, typename U> +struct PropertyCondition final : public PropertyConditionBase +{ + PropertyCondition() = delete; + ~PropertyCondition() = default; + PropertyCondition(const PropertyCondition&) = delete; + PropertyCondition& operator=(const PropertyCondition&) = delete; + PropertyCondition(PropertyCondition&&) = default; + PropertyCondition& operator=(PropertyCondition&&) = default; + + /** @brief Constructor + * + * The service argument can be nullptr. If something + * else is provided the function will call the the + * service directly. If omitted, the function will + * look up the service in the ObjectMapper. + * + * @param path - The path of the object containing + * the property to be tested. + * @param iface - The interface hosting the property + * to be tested. + * @param property - The property to be tested. + * @param condition - The test to run on the property. + * @param service - The DBus service hosting the object. + */ + PropertyCondition( + const char* path, + const char* iface, + const char* property, + U&& condition, + const char* service) : + PropertyConditionBase(path, iface, property, service), + _condition(std::forward<decltype(condition)>(condition)) {} + + /** @brief Test a property value. + * + * Make a DBus call and test the value of any property. + */ + bool eval(sdbusplus::message::message& msg) const override + { + sdbusplus::message::variant<T> value; + msg.read(value); + return _condition( + std::forward<T>(value.template get<T>())); + } + + private: + U _condition; +}; + } // namespace property_condition } // namespace details @@ -173,6 +287,24 @@ auto propertyChangedTo( iface, property, std::move(condition)); } +/** @brief Implicit type deduction for constructing PropertyCondition. */ +template <typename T> +auto propertyIs( + const char* path, + const char* iface, + const char* property, + T&& val, + const char* service = nullptr) +{ + auto condition = [val = std::forward<T>(val)](T && arg) + { + return arg == val; + }; + using U = decltype(condition); + return details::property_condition::PropertyCondition<T, U>( + path, iface, property, std::move(condition), service); +} + } // namespace filters } // namespace manager } // namespace inventory diff --git a/example/events.d/match2.yaml b/example/events.d/match2.yaml index 047462d..a4e98f0 100644 --- a/example/events.d/match2.yaml +++ b/example/events.d/match2.yaml @@ -49,4 +49,36 @@ events: paths: - /deleteme3 + - name: Example Match + description: > + Destroys the /deleteme3 object when the value of + ExampleProperty3 on /testing/trigger4 is 99 + and the value of ExampleProperty2 on /testing/trigger4 + changes to "123". + type: match + signatures: + - type: signal + path: /testing/trigger4 + interface: org.freedesktop.DBus.Properties + member: PropertiesChanged + filters: + - name: propertyChangedTo + interface: xyz.openbmc_project.Example.Iface2 + property: ExampleProperty2 + value: + value: 123 + type: string + - name: propertyIs + path: /testing/trigger4 + interface: xyz.openbmc_project.Example.Iface2 + property: ExampleProperty3 + service: phosphor.inventory.test.example + value: + value: 99 + type: int64 + actions: + - name: destroyObjects + paths: + - /deleteme3 + # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/test/test.cpp b/test/test.cpp index bd75dbb..e4b6a45 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -38,6 +38,9 @@ const auto trigger2 = sdbusplus::message::object_path(EXAMPLE_ROOT + "/trigger2"s); const auto trigger3 = sdbusplus::message::object_path(EXAMPLE_ROOT + "/trigger3"s); +const auto trigger4 = sdbusplus::message::object_path(EXAMPLE_ROOT + + "/trigger4"s); + const sdbusplus::message::object_path relDeleteMeOne{"/deleteme1"}; const sdbusplus::message::object_path relDeleteMeTwo{"/deleteme2"}; @@ -73,6 +76,8 @@ struct ExampleService ExampleIface1, ExampleIface2 > t2(bus, trigger2.str.c_str()); sdbusplus::server::object::object < ExampleIface1, ExampleIface2 > t3(bus, trigger3.str.c_str()); + sdbusplus::server::object::object < + ExampleIface1, ExampleIface2 > t4(bus, trigger4.str.c_str()); while (!shutdown) { @@ -233,6 +238,79 @@ void runTests() assert(!moreSignals); } + // Validate the propertyIs filter. + { + // Create an object to be deleted. + { + auto m = notify(); + m.append(relDeleteMeThree); + m.append(obj); + b.call(m); + } + + // Validate that the action does not run if the property doesn't match. + { + SignalQueue queue( + "path='" + root + "',member='InterfacesRemoved'"); + auto m = set(trigger4.str); + m.append("xyz.openbmc_project.Example.Iface2"); + m.append("ExampleProperty2"); + m.append(sdbusplus::message::variant<std::string>("123")); + b.call(m); + auto sig{queue.pop()}; + assert(!sig); + } + + // Validate that the action does run if the property matches. + { + // Set ExampleProperty2 to something else to the 123 filter + // matches. + SignalQueue queue( + "path='" + root + "',member='InterfacesRemoved'"); + auto m = set(trigger4.str); + m.append("xyz.openbmc_project.Example.Iface2"); + m.append("ExampleProperty2"); + m.append(sdbusplus::message::variant<std::string>("xyz")); + b.call(m); + auto sig{queue.pop()}; + assert(!sig); + } + { + // Set ExampleProperty3 to 99. + SignalQueue queue( + "path='" + root + "',member='InterfacesRemoved'"); + auto m = set(trigger4.str); + m.append("xyz.openbmc_project.Example.Iface2"); + m.append("ExampleProperty3"); + m.append(sdbusplus::message::variant<int64_t>(99)); + b.call(m); + auto sig{queue.pop()}; + assert(!sig); + } + { + SignalQueue queue( + "path='" + root + "',member='InterfacesRemoved'"); + auto m = set(trigger4.str); + m.append("xyz.openbmc_project.Example.Iface2"); + m.append("ExampleProperty2"); + m.append(sdbusplus::message::variant<std::string>("123")); + b.call(m); + + sdbusplus::message::object_path sigpath; + std::vector<std::string> interfaces; + { + std::vector<std::string> interfaces; + auto sig{queue.pop()}; + assert(sig); + sig.read(sigpath); + assert(sigpath == deleteMeThree); + sig.read(interfaces); + std::sort(interfaces.begin(), interfaces.end()); + assert(hasInterfaces(interfaces, obj)); + } + } + } + // Make sure DBus signals are handled. { // Create some objects to be deleted by an action. |