blob: 1be1d048e82a6294003a54f6750bf2e1dc2afb3e [file] [log] [blame]
Adriana Kobylak4772a942018-10-09 15:26:44 -05001#include "config.h"
2
3#include "msl_verify.hpp"
4
Brad Bishop8facccf2020-11-04 09:44:58 -05005#include <phosphor-logging/log.hpp>
6
Brad Bishop9f44c992020-11-06 14:48:46 -05007#include <filesystem>
Adriana Kobylak4772a942018-10-09 15:26:44 -05008#include <fstream>
Ed Tanous6b9daf52022-05-27 14:07:30 -07009#include <iterator>
Adriana Kobylak4772a942018-10-09 15:26:44 -050010#include <regex>
11
12namespace openpower
13{
14namespace software
15{
16namespace image
17{
18
Adriana Kobylak4772a942018-10-09 15:26:44 -050019using namespace phosphor::logging;
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -050020using AssociationList =
21 std::vector<std::tuple<std::string, std::string, std::string>>;
Adriana Kobylak4772a942018-10-09 15:26:44 -050022
23int MinimumShipLevel::compare(const Version& a, const Version& b)
24{
25 if (a.major < b.major)
26 {
27 return -1;
28 }
29 else if (a.major > b.major)
30 {
31 return 1;
32 }
33
34 if (a.minor < b.minor)
35 {
36 return -1;
37 }
38 else if (a.minor > b.minor)
39 {
40 return 1;
41 }
42
43 if (a.rev < b.rev)
44 {
45 return -1;
46 }
47 else if (a.rev > b.rev)
48 {
49 return 1;
50 }
51
52 return 0;
53}
54
55void MinimumShipLevel::parse(const std::string& versionStr, Version& version)
56{
57 std::smatch match;
58 version = {0, 0, 0};
59
John Wangb41a57d2019-09-16 15:40:21 +080060 // Match for vX.Y.Z or v-X.Y.Z
61 std::regex regex{"v-?([0-9]+)\\.([0-9]+)\\.([0-9]+)", std::regex::extended};
Adriana Kobylak4772a942018-10-09 15:26:44 -050062
63 if (!std::regex_search(versionStr, match, regex))
64 {
John Wangb41a57d2019-09-16 15:40:21 +080065 // Match for vX.Y or v-X.Y
66 std::regex regexShort{"v-?([0-9]+)\\.([0-9]+)", std::regex::extended};
Adriana Kobylak4772a942018-10-09 15:26:44 -050067 if (!std::regex_search(versionStr, match, regexShort))
68 {
69 log<level::ERR>("Unable to parse PNOR version",
70 entry("VERSION=%s", versionStr.c_str()));
71 return;
72 }
73 }
74 else
75 {
76 // Populate Z
77 version.rev = std::stoi(match[3]);
78 }
79 version.major = std::stoi(match[1]);
80 version.minor = std::stoi(match[2]);
81}
82
83std::string MinimumShipLevel::getFunctionalVersion()
84{
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -050085 auto bus = sdbusplus::bus::new_default();
86 auto method = bus.new_method_call(BUSNAME_UPDATER, SOFTWARE_OBJPATH,
87 SYSTEMD_PROPERTY_INTERFACE, "Get");
John Wangd05d4722019-09-11 15:18:15 +080088 method.append(ASSOCIATIONS_INTERFACE, "Associations");
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -050089 auto response = bus.call(method);
90
Patrick Williams212102e2020-05-13 17:50:50 -050091 std::variant<AssociationList> associations;
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -050092 try
93 {
94 response.read(associations);
95 }
Patrick Williams0dea1992022-07-22 19:26:52 -050096 catch (const sdbusplus::exception_t& e)
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -050097 {
98 log<level::ERR>("Failed to read software associations",
99 entry("ERROR=%s", e.what()),
100 entry("SIGNATURE=%s", response.get_signature()));
101 return {};
102 }
103
Patrick Williams550f31b2020-05-13 11:15:24 -0500104 auto& assocs = std::get<AssociationList>(associations);
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -0500105 if (assocs.empty())
Adriana Kobylak4772a942018-10-09 15:26:44 -0500106 {
107 return {};
108 }
109
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -0500110 for (const auto& assoc : assocs)
Adriana Kobylak4772a942018-10-09 15:26:44 -0500111 {
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -0500112 if (std::get<0>(assoc).compare(FUNCTIONAL_FWD_ASSOCIATION) == 0)
113 {
114 auto path = std::get<2>(assoc);
115 method = bus.new_method_call(BUSNAME_UPDATER, path.c_str(),
116 SYSTEMD_PROPERTY_INTERFACE, "Get");
117 method.append(VERSION_IFACE, "Version");
118 response = bus.call(method);
119
Patrick Williams212102e2020-05-13 17:50:50 -0500120 std::variant<std::string> functionalVersion;
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -0500121 try
122 {
123 response.read(functionalVersion);
Patrick Williams550f31b2020-05-13 11:15:24 -0500124 return std::get<std::string>(functionalVersion);
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -0500125 }
Patrick Williams0dea1992022-07-22 19:26:52 -0500126 catch (const sdbusplus::exception_t& e)
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -0500127 {
128 log<level::ERR>(
129 "Failed to read version property",
130 entry("ERROR=%s", e.what()),
131 entry("SIGNATURE=%s", response.get_signature()));
132 return {};
133 }
134 }
Adriana Kobylak4772a942018-10-09 15:26:44 -0500135 }
136
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -0500137 return {};
Adriana Kobylak4772a942018-10-09 15:26:44 -0500138}
139
140bool MinimumShipLevel::verify()
141{
142 if (minShipLevel.empty())
143 {
144 return true;
145 }
146
147 auto actual = getFunctionalVersion();
148 if (actual.empty())
149 {
150 return true;
151 }
152
Adriana Kobylak5c33b4c2018-10-17 20:16:19 -0500153 // Multiple min versions separated by a space can be specified, parse them
154 // into a vector, then sort them in ascending order
155 std::istringstream minStream(minShipLevel);
156 std::vector<std::string> mins(std::istream_iterator<std::string>{minStream},
157 std::istream_iterator<std::string>());
158 std::sort(mins.begin(), mins.end());
Adriana Kobylak4772a942018-10-09 15:26:44 -0500159
Adriana Kobylak5c33b4c2018-10-17 20:16:19 -0500160 // In order to handle non-continuous multiple min versions, need to compare
161 // the major.minor section first, then if they're the same, compare the rev.
162 // Ex: the min versions specified are 2.0.10 and 2.2. We need to pass if
163 // actual is 2.0.11 but fail if it's 2.1.x.
164 // 1. Save off the rev number to compare later if needed.
165 // 2. Zero out the rev number to just compare major and minor.
Adriana Kobylak4772a942018-10-09 15:26:44 -0500166 Version actualVersion = {0, 0, 0};
167 parse(actual, actualVersion);
Adriana Kobylak5c33b4c2018-10-17 20:16:19 -0500168 Version actualRev = {0, 0, actualVersion.rev};
169 actualVersion.rev = 0;
Adriana Kobylak4772a942018-10-09 15:26:44 -0500170
Adriana Kobylak5c33b4c2018-10-17 20:16:19 -0500171 auto rc = 0;
172 std::string tmpMin{};
173
174 for (auto const& min : mins)
175 {
176 tmpMin = min;
177
178 Version minVersion = {0, 0, 0};
179 parse(min, minVersion);
180 Version minRev = {0, 0, minVersion.rev};
181 minVersion.rev = 0;
182
183 rc = compare(actualVersion, minVersion);
184 if (rc < 0)
185 {
186 break;
187 }
188 else if (rc == 0)
189 {
190 // Same major.minor version, compare the rev
191 rc = compare(actualRev, minRev);
192 break;
193 }
194 }
Adriana Kobylak4772a942018-10-09 15:26:44 -0500195 if (rc < 0)
196 {
197 log<level::ERR>(
198 "PNOR Mininum Ship Level NOT met",
Adriana Kobylak5c33b4c2018-10-17 20:16:19 -0500199 entry("MIN_VERSION=%s", tmpMin.c_str()),
Adriana Kobylak4772a942018-10-09 15:26:44 -0500200 entry("ACTUAL_VERSION=%s", actual.c_str()),
201 entry("VERSION_PURPOSE=%s",
202 "xyz.openbmc_project.Software.Version.VersionPurpose.Host"));
203 return false;
204 }
205
206 return true;
207}
208
209} // namespace image
210} // namespace software
211} // namespace openpower