diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | mslverify/.gitignore | 1 | ||||
-rw-r--r-- | mslverify/Makefile.am | 15 | ||||
-rw-r--r-- | mslverify/README.md | 10 | ||||
-rw-r--r-- | mslverify/util.hpp | 225 | ||||
-rw-r--r-- | mslverify/verify.cpp | 114 |
7 files changed, 367 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am index af437a6..76fd2e6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1 +1 @@ -SUBDIRS = src +SUBDIRS = src mslverify diff --git a/configure.ac b/configure.ac index 5e03b54..593fbb7 100644 --- a/configure.ac +++ b/configure.ac @@ -53,5 +53,5 @@ AS_IF([test "x$enable_oe_sdk" == "xyes"], AC_ARG_VAR(YAML_PATH, [The path to the yaml config files.]) AS_IF([test "x$YAML_PATH" == "x"], [YAML_PATH="\${top_srcdir}/src/example"]) -AC_CONFIG_FILES([Makefile src/Makefile src/test/Makefile]) +AC_CONFIG_FILES([Makefile src/Makefile src/test/Makefile mslverify/Makefile]) AC_OUTPUT diff --git a/mslverify/.gitignore b/mslverify/.gitignore new file mode 100644 index 0000000..0bd39bd --- /dev/null +++ b/mslverify/.gitignore @@ -0,0 +1 @@ +phosphor-msl-verify diff --git a/mslverify/Makefile.am b/mslverify/Makefile.am new file mode 100644 index 0000000..4d79c0a --- /dev/null +++ b/mslverify/Makefile.am @@ -0,0 +1,15 @@ +AM_DEFAULT_SOURCE_EXT = .cpp +AM_CPPFLAGS = -iquote ${top_srcdir} + +sbin_PROGRAMS = phosphor-msl-verify + +phosphor_msl_verify_SOURCES = \ + verify.cpp +phosphor_msl_verify_LDADD = \ + $(SDBUSPLUS_LIBS) \ + $(PHOSPHOR_DBUS_INTERFACES_LIBS) \ + $(PHOSPHOR_LOGGING_LIBS) +phosphor_msl_verify_CXXFLAGS = \ + $(SDBUSPLUS_CFLAGS) \ + $(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \ + $(PHOSPHOR_LOGGING_CFLAGS) diff --git a/mslverify/README.md b/mslverify/README.md new file mode 100644 index 0000000..612561e --- /dev/null +++ b/mslverify/README.md @@ -0,0 +1,10 @@ +phosphor-msl-verify + +phosphor-msl-verify is a "oneshot" application for basic minimum ship level +[(MSL)](https://github.com/openbmc/phosphor-dbus-interfaces/xyz/openbmc_project/control/README.msl) +verification. + +The application first determines if MSL validation is disabled and if not, +searches the D-Bus object namespace for any MeetsMSL interfaces and exits with +non-zero status if any inventory items implementing the interface are found +that do not meet the MSL. diff --git a/mslverify/util.hpp b/mslverify/util.hpp new file mode 100644 index 0000000..2c26fc2 --- /dev/null +++ b/mslverify/util.hpp @@ -0,0 +1,225 @@ +#pragma once + +#include <sdbusplus/bus.hpp> +#include <sdbusplus/message.hpp> +#include <phosphor-logging/log.hpp> +#include <phosphor-logging/elog.hpp> +#include <phosphor-logging/elog-errors.hpp> +#include <xyz/openbmc_project/Common/error.hpp> + +namespace util +{ +namespace detail +{ +namespace errors = sdbusplus::xyz::openbmc_project::Common::Error; +} // namespace detail + +/** @brief Alias for PropertiesChanged signal callbacks. */ +template <typename ...T> +using Properties = std::map<std::string, sdbusplus::message::variant<T...>>; + +namespace sdbusplus +{ + +/** @brief Get the bus connection. */ +static auto& getBus() __attribute__((pure)); +static auto& getBus() +{ + static auto bus = ::sdbusplus::bus::new_default(); + return bus; +} + +/** @brief Invoke a method. */ +template <typename ...Args> +static auto callMethod( + ::sdbusplus::bus::bus& bus, + const std::string& busName, + const std::string& path, + const std::string& interface, + const std::string& method, + Args&& ... args) +{ + auto reqMsg = bus.new_method_call( + busName.c_str(), + path.c_str(), + interface.c_str(), + method.c_str()); + reqMsg.append(std::forward<Args>(args)...); + auto respMsg = bus.call(reqMsg); + + if (respMsg.is_method_error()) + { + phosphor::logging::log<phosphor::logging::level::INFO>( + "Failed to invoke DBus method.", + phosphor::logging::entry("PATH=%s", path.c_str()), + phosphor::logging::entry( + "INTERFACE=%s", interface.c_str()), + phosphor::logging::entry("METHOD=%s", method.c_str())); + phosphor::logging::elog<detail::errors::InternalFailure>(); + } + + return respMsg; +} + +/** @brief Invoke a method. */ +template <typename ...Args> +static auto callMethod( + const std::string& busName, + const std::string& path, + const std::string& interface, + const std::string& method, + Args&& ... args) +{ + return callMethod( + getBus(), + busName, + path, + interface, + method, + std::forward<Args>(args)...); +} + +/** @brief Invoke a method and read the response. */ +template <typename Ret, typename ...Args> +static auto callMethodAndRead( + ::sdbusplus::bus::bus& bus, + const std::string& busName, + const std::string& path, + const std::string& interface, + const std::string& method, + Args&& ... args) +{ + ::sdbusplus::message::message respMsg = + callMethod<Args...>( + bus, + busName, + path, + interface, + method, + std::forward<Args>(args)...); + Ret resp; + respMsg.read(resp); + return resp; +} + +/** @brief Invoke a method and read the response. */ + template <typename Ret, typename ...Args> +static auto callMethodAndRead( + const std::string& busName, + const std::string& path, + const std::string& interface, + const std::string& method, + Args&& ... args) +{ + return callMethodAndRead<Ret>( + getBus(), + busName, + path, + interface, + method, + std::forward<Args>(args)...); +} + + +/** @brief Get service from the mapper. */ +static auto getService( + ::sdbusplus::bus::bus& bus, + const std::string& path, + const std::string& interface) +{ + using namespace std::literals::string_literals; + using GetObject = std::map<std::string, std::vector<std::string>>; + + auto mapperResp = callMethodAndRead<GetObject>( + bus, + "xyz.openbmc_project.ObjectMapper"s, + "/xyz/openbmc_project/object_mapper"s, + "xyz.openbmc_project.ObjectMapper"s, + "GetObject"s, + path, + GetObject::mapped_type{interface}); + + if (mapperResp.empty()) + { + phosphor::logging::log<phosphor::logging::level::INFO>( + "Object not found.", + phosphor::logging::entry("PATH=%s", path.c_str()), + phosphor::logging::entry( + "INTERFACE=%s", interface.c_str())); + phosphor::logging::elog<detail::errors::InternalFailure>(); + } + return mapperResp.begin()->first; +} + +/** @brief Get a property without mapper lookup. */ +template <typename Property> +static auto getProperty( + ::sdbusplus::bus::bus& bus, + const std::string& busName, + const std::string& path, + const std::string& interface, + const std::string& property) +{ + using namespace std::literals::string_literals; + + auto msg = callMethod( + bus, + busName, + path, + "org.freedesktop.DBus.Properties"s, + "Get"s, + interface, + property); + ::sdbusplus::message::variant<Property> value; + msg.read(value); + return value.template get<Property>(); +} + +/** @brief Get a property without mapper lookup. */ +template <typename Property> +static auto getProperty( + const std::string& busName, + const std::string& path, + const std::string& interface, + const std::string& property) +{ + return getProperty<Property>( + getBus(), + busName, + path, + interface, + property); +} + +/** @brief Get a property with mapper lookup. */ +template <typename Property> +static auto getProperty( + ::sdbusplus::bus::bus& bus, + const std::string& path, + const std::string& interface, + const std::string& property) +{ + return getProperty<Property>( + bus, + getService(bus, path, interface), + path, + interface, + property); +} + +/** @brief Get a property with mapper lookup. */ +template <typename Property> +static auto getProperty( + const std::string& path, + const std::string& interface, + const std::string& property) +{ + return getProperty<Property>( + getBus(), + path, + interface, + property); +} + +} // namespace sdbusplus +} // namespace util diff --git a/mslverify/verify.cpp b/mslverify/verify.cpp new file mode 100644 index 0000000..1595166 --- /dev/null +++ b/mslverify/verify.cpp @@ -0,0 +1,114 @@ +/** + * 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 <algorithm> +#include <map> +#include <string> +#include "util.hpp" + +using namespace std::literals::string_literals; + +template <typename T> +struct BusMeetsMSL +{ + std::string path; + + BusMeetsMSL(const std::string& p) + :path(p) {} + + auto operator()(const T& arg) + { + // Query each service hosting + // xyz.openbmc_project.Inventory.Decorator.MeetsMinimumShipLevel. + + const auto& busName = arg.first; + return util::sdbusplus::getProperty<bool>( + busName, + path, + "xyz.openbmc_project.Inventory." + "Decorator.MeetsMinimumShipLevel"s, + "MeetsMinimumShipLevel"s); + } +}; + +template <typename T> +struct PathMeetsMSL +{ + auto operator()(const T& arg) + { + // A given path in the mapper response is composed of + // a map of services/interfaces. Validate each service + // that hosts the MSL interface meets the MSL. + + const auto& path = arg.first; + return std::all_of( + arg.second.begin(), + arg.second.end(), + BusMeetsMSL<typename decltype(arg.second)::value_type>(path)); + } +}; + +int main(void) +{ + auto mslVerificationRequired = + util::sdbusplus::getProperty<bool>( + "/xyz/openbmc_project/control/minimum_ship_level_required"s, + "xyz.openbmc_project.Control.MinimumShipLevel"s, + "MinimumShipLevelRequired"s); + + if (!mslVerificationRequired) + { + return 0; + } + + // Obtain references to all objects hosting + // xyz.openbmc_project.Inventory.Decorator.MeetsMinimumShipLevel + // with a mapper subtree query. For each object, validate that + // the minimum ship level has been met. + + using SubTreeType = + std::map< + std::string, + std::map<std::string, std::vector<std::string>>>; + + auto subtree = + util::sdbusplus::callMethodAndRead<SubTreeType>( + "xyz.openbmc_project.ObjectMapper"s, + "/xyz/openbmc_project/object_mapper"s, + "xyz.openbmc_project.ObjectMapper"s, + "GetSubTree"s, + "/"s, + 0, + std::vector<std::string>{ + "xyz.openbmc_project.Inventory" + ".Decorator.MeetsMinimumShipLevel"s}); + + auto result = std::all_of( + subtree.begin(), + subtree.end(), + PathMeetsMSL<SubTreeType::value_type>()); + + if (!result) + { + phosphor::logging::log<phosphor::logging::level::INFO>( + "The physical system configuration does not " + "satisfy the minimum ship level."); + + return 1; + } + + return 0; +} |