blob: dc4238ce1f2d33164f357df8b927135347e976c3 [file] [log] [blame]
Adriana Kobylak4772a942018-10-09 15:26:44 -05001#include "config.h"
2
3#include "msl_verify.hpp"
4
5#include <experimental/filesystem>
6#include <fstream>
7#include <phosphor-logging/log.hpp>
8#include <regex>
9
10namespace openpower
11{
12namespace software
13{
14namespace image
15{
16
17namespace fs = std::experimental::filesystem;
William A. Kennington IIIaf07acf2018-11-06 15:15:28 -080018namespace variant_ns = sdbusplus::message::variant_ns;
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
60 // Match for vX.Y.Z
61 std::regex regex{"v([0-9]+)\\.([0-9]+)\\.([0-9]+)", std::regex::extended};
62
63 if (!std::regex_search(versionStr, match, regex))
64 {
65 // Match for vX.Y
66 std::regex regexShort{"v([0-9]+)\\.([0-9]+)", std::regex::extended};
67 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");
88 method.append(ASSOCIATIONS_INTERFACE, "associations");
89 auto response = bus.call(method);
90
91 sdbusplus::message::variant<AssociationList> associations;
92 try
93 {
94 response.read(associations);
95 }
96 catch (const sdbusplus::exception::SdBusError& e)
97 {
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
William A. Kennington IIIaf07acf2018-11-06 15:15:28 -0800104 auto& assocs = variant_ns::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
120 sdbusplus::message::variant<std::string> functionalVersion;
121 try
122 {
123 response.read(functionalVersion);
William A. Kennington IIIaf07acf2018-11-06 15:15:28 -0800124 return variant_ns::get<std::string>(functionalVersion);
Adriana Kobylakea4e6ae2018-10-30 12:17:31 -0500125 }
126 catch (const sdbusplus::exception::SdBusError& e)
127 {
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