14772a944SAdriana Kobylak #include "config.h"
24772a944SAdriana Kobylak
34772a944SAdriana Kobylak #include "msl_verify.hpp"
44772a944SAdriana Kobylak
58facccfaSBrad Bishop #include <phosphor-logging/log.hpp>
68facccfaSBrad Bishop
79f44c99aSBrad Bishop #include <filesystem>
84772a944SAdriana Kobylak #include <fstream>
96b9daf56SEd Tanous #include <iterator>
104772a944SAdriana Kobylak #include <regex>
114772a944SAdriana Kobylak
124772a944SAdriana Kobylak namespace openpower
134772a944SAdriana Kobylak {
144772a944SAdriana Kobylak namespace software
154772a944SAdriana Kobylak {
164772a944SAdriana Kobylak namespace image
174772a944SAdriana Kobylak {
184772a944SAdriana Kobylak
194772a944SAdriana Kobylak using namespace phosphor::logging;
20ea4e6aeeSAdriana Kobylak using AssociationList =
21ea4e6aeeSAdriana Kobylak std::vector<std::tuple<std::string, std::string, std::string>>;
224772a944SAdriana Kobylak
compare(const Version & a,const Version & b)234772a944SAdriana Kobylak int MinimumShipLevel::compare(const Version& a, const Version& b)
244772a944SAdriana Kobylak {
254772a944SAdriana Kobylak if (a.major < b.major)
264772a944SAdriana Kobylak {
274772a944SAdriana Kobylak return -1;
284772a944SAdriana Kobylak }
294772a944SAdriana Kobylak else if (a.major > b.major)
304772a944SAdriana Kobylak {
314772a944SAdriana Kobylak return 1;
324772a944SAdriana Kobylak }
334772a944SAdriana Kobylak
344772a944SAdriana Kobylak if (a.minor < b.minor)
354772a944SAdriana Kobylak {
364772a944SAdriana Kobylak return -1;
374772a944SAdriana Kobylak }
384772a944SAdriana Kobylak else if (a.minor > b.minor)
394772a944SAdriana Kobylak {
404772a944SAdriana Kobylak return 1;
414772a944SAdriana Kobylak }
424772a944SAdriana Kobylak
434772a944SAdriana Kobylak if (a.rev < b.rev)
444772a944SAdriana Kobylak {
454772a944SAdriana Kobylak return -1;
464772a944SAdriana Kobylak }
474772a944SAdriana Kobylak else if (a.rev > b.rev)
484772a944SAdriana Kobylak {
494772a944SAdriana Kobylak return 1;
504772a944SAdriana Kobylak }
514772a944SAdriana Kobylak
524772a944SAdriana Kobylak return 0;
534772a944SAdriana Kobylak }
544772a944SAdriana Kobylak
parse(const std::string & versionStr,Version & version)554772a944SAdriana Kobylak void MinimumShipLevel::parse(const std::string& versionStr, Version& version)
564772a944SAdriana Kobylak {
574772a944SAdriana Kobylak std::smatch match;
584772a944SAdriana Kobylak version = {0, 0, 0};
594772a944SAdriana Kobylak
60b41a57d5SJohn Wang // Match for vX.Y.Z or v-X.Y.Z
61b41a57d5SJohn Wang std::regex regex{"v-?([0-9]+)\\.([0-9]+)\\.([0-9]+)", std::regex::extended};
624772a944SAdriana Kobylak
634772a944SAdriana Kobylak if (!std::regex_search(versionStr, match, regex))
644772a944SAdriana Kobylak {
65b41a57d5SJohn Wang // Match for vX.Y or v-X.Y
66b41a57d5SJohn Wang std::regex regexShort{"v-?([0-9]+)\\.([0-9]+)", std::regex::extended};
674772a944SAdriana Kobylak if (!std::regex_search(versionStr, match, regexShort))
684772a944SAdriana Kobylak {
694772a944SAdriana Kobylak log<level::ERR>("Unable to parse PNOR version",
704772a944SAdriana Kobylak entry("VERSION=%s", versionStr.c_str()));
714772a944SAdriana Kobylak return;
724772a944SAdriana Kobylak }
734772a944SAdriana Kobylak }
744772a944SAdriana Kobylak else
754772a944SAdriana Kobylak {
764772a944SAdriana Kobylak // Populate Z
774772a944SAdriana Kobylak version.rev = std::stoi(match[3]);
784772a944SAdriana Kobylak }
794772a944SAdriana Kobylak version.major = std::stoi(match[1]);
804772a944SAdriana Kobylak version.minor = std::stoi(match[2]);
814772a944SAdriana Kobylak }
824772a944SAdriana Kobylak
getFunctionalVersion()834772a944SAdriana Kobylak std::string MinimumShipLevel::getFunctionalVersion()
844772a944SAdriana Kobylak {
85ea4e6aeeSAdriana Kobylak auto bus = sdbusplus::bus::new_default();
86ea4e6aeeSAdriana Kobylak auto method = bus.new_method_call(BUSNAME_UPDATER, SOFTWARE_OBJPATH,
87ea4e6aeeSAdriana Kobylak SYSTEMD_PROPERTY_INTERFACE, "Get");
88d05d4725SJohn Wang method.append(ASSOCIATIONS_INTERFACE, "Associations");
89ea4e6aeeSAdriana Kobylak auto response = bus.call(method);
90ea4e6aeeSAdriana Kobylak
91212102e6SPatrick Williams std::variant<AssociationList> associations;
92ea4e6aeeSAdriana Kobylak try
93ea4e6aeeSAdriana Kobylak {
94ea4e6aeeSAdriana Kobylak response.read(associations);
95ea4e6aeeSAdriana Kobylak }
960dea1992SPatrick Williams catch (const sdbusplus::exception_t& e)
97ea4e6aeeSAdriana Kobylak {
98ea4e6aeeSAdriana Kobylak log<level::ERR>("Failed to read software associations",
99ea4e6aeeSAdriana Kobylak entry("ERROR=%s", e.what()),
100ea4e6aeeSAdriana Kobylak entry("SIGNATURE=%s", response.get_signature()));
101ea4e6aeeSAdriana Kobylak return {};
102ea4e6aeeSAdriana Kobylak }
103ea4e6aeeSAdriana Kobylak
104550f31b3SPatrick Williams auto& assocs = std::get<AssociationList>(associations);
105ea4e6aeeSAdriana Kobylak if (assocs.empty())
1064772a944SAdriana Kobylak {
1074772a944SAdriana Kobylak return {};
1084772a944SAdriana Kobylak }
1094772a944SAdriana Kobylak
110ea4e6aeeSAdriana Kobylak for (const auto& assoc : assocs)
1114772a944SAdriana Kobylak {
112ea4e6aeeSAdriana Kobylak if (std::get<0>(assoc).compare(FUNCTIONAL_FWD_ASSOCIATION) == 0)
113ea4e6aeeSAdriana Kobylak {
114ea4e6aeeSAdriana Kobylak auto path = std::get<2>(assoc);
115ea4e6aeeSAdriana Kobylak method = bus.new_method_call(BUSNAME_UPDATER, path.c_str(),
116ea4e6aeeSAdriana Kobylak SYSTEMD_PROPERTY_INTERFACE, "Get");
117ea4e6aeeSAdriana Kobylak method.append(VERSION_IFACE, "Version");
118ea4e6aeeSAdriana Kobylak response = bus.call(method);
119ea4e6aeeSAdriana Kobylak
120212102e6SPatrick Williams std::variant<std::string> functionalVersion;
121ea4e6aeeSAdriana Kobylak try
122ea4e6aeeSAdriana Kobylak {
123ea4e6aeeSAdriana Kobylak response.read(functionalVersion);
124550f31b3SPatrick Williams return std::get<std::string>(functionalVersion);
125ea4e6aeeSAdriana Kobylak }
1260dea1992SPatrick Williams catch (const sdbusplus::exception_t& e)
127ea4e6aeeSAdriana Kobylak {
128ea4e6aeeSAdriana Kobylak log<level::ERR>(
129ea4e6aeeSAdriana Kobylak "Failed to read version property",
130ea4e6aeeSAdriana Kobylak entry("ERROR=%s", e.what()),
131ea4e6aeeSAdriana Kobylak entry("SIGNATURE=%s", response.get_signature()));
1324772a944SAdriana Kobylak return {};
1334772a944SAdriana Kobylak }
134ea4e6aeeSAdriana Kobylak }
135ea4e6aeeSAdriana Kobylak }
1364772a944SAdriana Kobylak
137ea4e6aeeSAdriana Kobylak return {};
1384772a944SAdriana Kobylak }
1394772a944SAdriana Kobylak
verify()1404772a944SAdriana Kobylak bool MinimumShipLevel::verify()
1414772a944SAdriana Kobylak {
1424772a944SAdriana Kobylak if (minShipLevel.empty())
1434772a944SAdriana Kobylak {
1444772a944SAdriana Kobylak return true;
1454772a944SAdriana Kobylak }
1464772a944SAdriana Kobylak
1474772a944SAdriana Kobylak auto actual = getFunctionalVersion();
1484772a944SAdriana Kobylak if (actual.empty())
1494772a944SAdriana Kobylak {
1504772a944SAdriana Kobylak return true;
1514772a944SAdriana Kobylak }
1524772a944SAdriana Kobylak
1535c33b4c6SAdriana Kobylak // Multiple min versions separated by a space can be specified, parse them
1545c33b4c6SAdriana Kobylak // into a vector, then sort them in ascending order
1555c33b4c6SAdriana Kobylak std::istringstream minStream(minShipLevel);
1565c33b4c6SAdriana Kobylak std::vector<std::string> mins(std::istream_iterator<std::string>{minStream},
1575c33b4c6SAdriana Kobylak std::istream_iterator<std::string>());
1585c33b4c6SAdriana Kobylak std::sort(mins.begin(), mins.end());
1594772a944SAdriana Kobylak
1605c33b4c6SAdriana Kobylak // In order to handle non-continuous multiple min versions, need to compare
1615c33b4c6SAdriana Kobylak // the major.minor section first, then if they're the same, compare the rev.
1625c33b4c6SAdriana Kobylak // Ex: the min versions specified are 2.0.10 and 2.2. We need to pass if
1635c33b4c6SAdriana Kobylak // actual is 2.0.11 but fail if it's 2.1.x.
1645c33b4c6SAdriana Kobylak // 1. Save off the rev number to compare later if needed.
1655c33b4c6SAdriana Kobylak // 2. Zero out the rev number to just compare major and minor.
1664772a944SAdriana Kobylak Version actualVersion = {0, 0, 0};
1674772a944SAdriana Kobylak parse(actual, actualVersion);
1685c33b4c6SAdriana Kobylak Version actualRev = {0, 0, actualVersion.rev};
1695c33b4c6SAdriana Kobylak actualVersion.rev = 0;
1704772a944SAdriana Kobylak
1715c33b4c6SAdriana Kobylak auto rc = 0;
1725c33b4c6SAdriana Kobylak std::string tmpMin{};
1735c33b4c6SAdriana Kobylak
1747fb6c346SPatrick Williams for (const auto& min : mins)
1755c33b4c6SAdriana Kobylak {
1765c33b4c6SAdriana Kobylak tmpMin = min;
1775c33b4c6SAdriana Kobylak
1785c33b4c6SAdriana Kobylak Version minVersion = {0, 0, 0};
1795c33b4c6SAdriana Kobylak parse(min, minVersion);
1805c33b4c6SAdriana Kobylak Version minRev = {0, 0, minVersion.rev};
1815c33b4c6SAdriana Kobylak minVersion.rev = 0;
1825c33b4c6SAdriana Kobylak
1835c33b4c6SAdriana Kobylak rc = compare(actualVersion, minVersion);
1845c33b4c6SAdriana Kobylak if (rc < 0)
1855c33b4c6SAdriana Kobylak {
1865c33b4c6SAdriana Kobylak break;
1875c33b4c6SAdriana Kobylak }
1885c33b4c6SAdriana Kobylak else if (rc == 0)
1895c33b4c6SAdriana Kobylak {
1905c33b4c6SAdriana Kobylak // Same major.minor version, compare the rev
1915c33b4c6SAdriana Kobylak rc = compare(actualRev, minRev);
1925c33b4c6SAdriana Kobylak break;
1935c33b4c6SAdriana Kobylak }
1945c33b4c6SAdriana Kobylak }
1954772a944SAdriana Kobylak if (rc < 0)
1964772a944SAdriana Kobylak {
1974772a944SAdriana Kobylak log<level::ERR>(
198*96442c88SManojkiran Eda "PNOR Minimum Ship Level NOT met",
1995c33b4c6SAdriana Kobylak entry("MIN_VERSION=%s", tmpMin.c_str()),
2004772a944SAdriana Kobylak entry("ACTUAL_VERSION=%s", actual.c_str()),
2014772a944SAdriana Kobylak entry("VERSION_PURPOSE=%s",
2024772a944SAdriana Kobylak "xyz.openbmc_project.Software.Version.VersionPurpose.Host"));
2034772a944SAdriana Kobylak return false;
2044772a944SAdriana Kobylak }
2054772a944SAdriana Kobylak
2064772a944SAdriana Kobylak return true;
2074772a944SAdriana Kobylak }
2084772a944SAdriana Kobylak
2094772a944SAdriana Kobylak } // namespace image
2104772a944SAdriana Kobylak } // namespace software
2114772a944SAdriana Kobylak } // namespace openpower
212