blob: 36d4ffe888e1e1a77897b2155f064aadcef5c48a [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>
Adriana Kobylak4772a942018-10-09 15:26:44 -05009#include <regex>
10
11namespace openpower
12{
13namespace software
14{
15namespace image
16{
17
Adriana Kobylak4772a942018-10-09 15:26:44 -050018using namespace phosphor::logging;
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -050019using AssociationList =
20 std::vector<std::tuple<std::string, std::string, std::string>>;
Adriana Kobylak4772a942018-10-09 15:26:44 -050021
22int MinimumShipLevel::compare(const Version& a, const Version& b)
23{
24 if (a.major < b.major)
25 {
26 return -1;
27 }
28 else if (a.major > b.major)
29 {
30 return 1;
31 }
32
33 if (a.minor < b.minor)
34 {
35 return -1;
36 }
37 else if (a.minor > b.minor)
38 {
39 return 1;
40 }
41
42 if (a.rev < b.rev)
43 {
44 return -1;
45 }
46 else if (a.rev > b.rev)
47 {
48 return 1;
49 }
50
51 return 0;
52}
53
54void MinimumShipLevel::parse(const std::string& versionStr, Version& version)
55{
56 std::smatch match;
57 version = {0, 0, 0};
58
John Wangb41a57d2019-09-16 15:40:21 +080059 // Match for vX.Y.Z or v-X.Y.Z
60 std::regex regex{"v-?([0-9]+)\\.([0-9]+)\\.([0-9]+)", std::regex::extended};
Adriana Kobylak4772a942018-10-09 15:26:44 -050061
62 if (!std::regex_search(versionStr, match, regex))
63 {
John Wangb41a57d2019-09-16 15:40:21 +080064 // Match for vX.Y or v-X.Y
65 std::regex regexShort{"v-?([0-9]+)\\.([0-9]+)", std::regex::extended};
Adriana Kobylak4772a942018-10-09 15:26:44 -050066 if (!std::regex_search(versionStr, match, regexShort))
67 {
68 log<level::ERR>("Unable to parse PNOR version",
69 entry("VERSION=%s", versionStr.c_str()));
70 return;
71 }
72 }
73 else
74 {
75 // Populate Z
76 version.rev = std::stoi(match[3]);
77 }
78 version.major = std::stoi(match[1]);
79 version.minor = std::stoi(match[2]);
80}
81
82std::string MinimumShipLevel::getFunctionalVersion()
83{
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -050084 auto bus = sdbusplus::bus::new_default();
85 auto method = bus.new_method_call(BUSNAME_UPDATER, SOFTWARE_OBJPATH,
86 SYSTEMD_PROPERTY_INTERFACE, "Get");
John Wangd05d4722019-09-11 15:18:15 +080087 method.append(ASSOCIATIONS_INTERFACE, "Associations");
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -050088 auto response = bus.call(method);
89
Patrick Williams212102e2020-05-13 17:50:50 -050090 std::variant<AssociationList> associations;
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -050091 try
92 {
93 response.read(associations);
94 }
95 catch (const sdbusplus::exception::SdBusError& e)
96 {
97 log<level::ERR>("Failed to read software associations",
98 entry("ERROR=%s", e.what()),
99 entry("SIGNATURE=%s", response.get_signature()));
100 return {};
101 }
102
Patrick Williams550f31b2020-05-13 11:15:24 -0500103 auto& assocs = std::get<AssociationList>(associations);
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -0500104 if (assocs.empty())
Adriana Kobylak4772a942018-10-09 15:26:44 -0500105 {
106 return {};
107 }
108
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -0500109 for (const auto& assoc : assocs)
Adriana Kobylak4772a942018-10-09 15:26:44 -0500110 {
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -0500111 if (std::get<0>(assoc).compare(FUNCTIONAL_FWD_ASSOCIATION) == 0)
112 {
113 auto path = std::get<2>(assoc);
114 method = bus.new_method_call(BUSNAME_UPDATER, path.c_str(),
115 SYSTEMD_PROPERTY_INTERFACE, "Get");
116 method.append(VERSION_IFACE, "Version");
117 response = bus.call(method);
118
Patrick Williams212102e2020-05-13 17:50:50 -0500119 std::variant<std::string> functionalVersion;
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -0500120 try
121 {
122 response.read(functionalVersion);
Patrick Williams550f31b2020-05-13 11:15:24 -0500123 return std::get<std::string>(functionalVersion);
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -0500124 }
125 catch (const sdbusplus::exception::SdBusError& e)
126 {
127 log<level::ERR>(
128 "Failed to read version property",
129 entry("ERROR=%s", e.what()),
130 entry("SIGNATURE=%s", response.get_signature()));
131 return {};
132 }
133 }
Adriana Kobylak4772a942018-10-09 15:26:44 -0500134 }
135
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -0500136 return {};
Adriana Kobylak4772a942018-10-09 15:26:44 -0500137}
138
139bool MinimumShipLevel::verify()
140{
141 if (minShipLevel.empty())
142 {
143 return true;
144 }
145
146 auto actual = getFunctionalVersion();
147 if (actual.empty())
148 {
149 return true;
150 }
151
Adriana Kobylak5c33b4c2018-10-17 20:16:19 -0500152 // Multiple min versions separated by a space can be specified, parse them
153 // into a vector, then sort them in ascending order
154 std::istringstream minStream(minShipLevel);
155 std::vector<std::string> mins(std::istream_iterator<std::string>{minStream},
156 std::istream_iterator<std::string>());
157 std::sort(mins.begin(), mins.end());
Adriana Kobylak4772a942018-10-09 15:26:44 -0500158
Adriana Kobylak5c33b4c2018-10-17 20:16:19 -0500159 // In order to handle non-continuous multiple min versions, need to compare
160 // the major.minor section first, then if they're the same, compare the rev.
161 // Ex: the min versions specified are 2.0.10 and 2.2. We need to pass if
162 // actual is 2.0.11 but fail if it's 2.1.x.
163 // 1. Save off the rev number to compare later if needed.
164 // 2. Zero out the rev number to just compare major and minor.
Adriana Kobylak4772a942018-10-09 15:26:44 -0500165 Version actualVersion = {0, 0, 0};
166 parse(actual, actualVersion);
Adriana Kobylak5c33b4c2018-10-17 20:16:19 -0500167 Version actualRev = {0, 0, actualVersion.rev};
168 actualVersion.rev = 0;
Adriana Kobylak4772a942018-10-09 15:26:44 -0500169
Adriana Kobylak5c33b4c2018-10-17 20:16:19 -0500170 auto rc = 0;
171 std::string tmpMin{};
172
173 for (auto const& min : mins)
174 {
175 tmpMin = min;
176
177 Version minVersion = {0, 0, 0};
178 parse(min, minVersion);
179 Version minRev = {0, 0, minVersion.rev};
180 minVersion.rev = 0;
181
182 rc = compare(actualVersion, minVersion);
183 if (rc < 0)
184 {
185 break;
186 }
187 else if (rc == 0)
188 {
189 // Same major.minor version, compare the rev
190 rc = compare(actualRev, minRev);
191 break;
192 }
193 }
Adriana Kobylak4772a942018-10-09 15:26:44 -0500194 if (rc < 0)
195 {
196 log<level::ERR>(
197 "PNOR Mininum Ship Level NOT met",
Adriana Kobylak5c33b4c2018-10-17 20:16:19 -0500198 entry("MIN_VERSION=%s", tmpMin.c_str()),
Adriana Kobylak4772a942018-10-09 15:26:44 -0500199 entry("ACTUAL_VERSION=%s", actual.c_str()),
200 entry("VERSION_PURPOSE=%s",
201 "xyz.openbmc_project.Software.Version.VersionPurpose.Host"));
202 return false;
203 }
204
205 return true;
206}
207
208} // namespace image
209} // namespace software
210} // namespace openpower