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