frudevice: add i2c bus blacklist json
The fru-device daemon contained a black list internally of buses to not
rescan in the future. This list was generated automatically by talking
to each bus. On a system where scanning the devices on a specific bus
can cause problems, extend that blacklist to be something that can be
specified for a machine.
This adds a file blacklist.json, which currently contains one field. An
array of bus integers named "buses." This file can be overwritten by a
platform recipe to drop in a list of buses to avoid scanning.
Tested: Verified that an empty, but valid json file (the default file
has no effect).
Tested: Verified that the array with an invalid type of entry is caught,
reported and the program exited.
Tested: Verified that the array with three valid entries prevents the
daemon from scanning the devices on those buses.
Signed-off-by: Patrick Venture <venture@google.com>
Change-Id: I1c426ebed2f7ac073ee45ed8b205e4a176a519ab
diff --git a/src/FruDevice.cpp b/src/FruDevice.cpp
index 0f1350c..0ef0e94 100644
--- a/src/FruDevice.cpp
+++ b/src/FruDevice.cpp
@@ -27,9 +27,11 @@
#include <fstream>
#include <future>
#include <iostream>
+#include <nlohmann/json.hpp>
#include <regex>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
+#include <string>
#include <thread>
#include <variant>
@@ -45,6 +47,8 @@
constexpr size_t MAX_EEPROM_PAGE_INDEX = 255;
constexpr size_t busTimeoutSeconds = 5;
+constexpr const char* blacklistPath = PACKAGE_DIR "blacklist.json";
+
const static constexpr char* BASEBOARD_FRU_LOCATION =
"/etc/fru/baseboard.fru.bin";
@@ -275,6 +279,69 @@
return future.get();
}
+void loadBlacklist(const char* path)
+{
+ std::ifstream blacklistStream(path);
+ if (!blacklistStream.good())
+ {
+ // File is optional.
+ std::cerr << "Cannot open blacklist file.\n\n";
+ return;
+ }
+
+ nlohmann::json data =
+ nlohmann::json::parse(blacklistStream, nullptr, false);
+ if (data.is_discarded())
+ {
+ std::cerr << "Illegal blacklist file detected, cannot validate JSON, "
+ "exiting\n";
+ std::exit(EXIT_FAILURE);
+ return;
+ }
+
+ // It's expected to have at least one field, "buses" that is an array of the
+ // buses by integer. Allow for future options to exclude further aspects,
+ // such as specific addresses or ranges.
+ if (data.type() != nlohmann::json::value_t::object)
+ {
+ std::cerr << "Illegal blacklist, expected to read dictionary\n";
+ std::exit(EXIT_FAILURE);
+ return;
+ }
+
+ // If buses field is missing, that's fine.
+ if (data.count("buses") == 1)
+ {
+ // Parse the buses array after a little validation.
+ auto buses = data.at("buses");
+ if (buses.type() != nlohmann::json::value_t::array)
+ {
+ // Buses field present but invalid, therefore this is an error.
+ std::cerr << "Invalid contents for blacklist buses field\n";
+ std::exit(EXIT_FAILURE);
+ return;
+ }
+
+ // Catch exception here for type mis-match.
+ try
+ {
+ for (const auto& bus : buses)
+ {
+ busBlacklist.insert(bus.get<size_t>());
+ }
+ }
+ catch (const nlohmann::detail::type_error& e)
+ {
+ // Type mis-match is a critical error.
+ std::cerr << "Invalid bus type: " << e.what() << "\n";
+ std::exit(EXIT_FAILURE);
+ return;
+ }
+ }
+
+ return;
+}
+
static void FindI2CDevices(const std::vector<fs::path>& i2cBuses,
BusMap& busmap)
{
@@ -818,6 +885,9 @@
return 1;
}
+ // check for and load blacklist with initial buses.
+ loadBlacklist(blacklistPath);
+
boost::asio::io_service io;
auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
auto objServer = sdbusplus::asio::object_server(systemBus);