Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 1 | #include "config.h" |
| 2 | |
| 3 | #include "msl_verify.hpp" |
| 4 | |
Brad Bishop | 8facccf | 2020-11-04 09:44:58 -0500 | [diff] [blame] | 5 | #include <phosphor-logging/log.hpp> |
| 6 | |
Brad Bishop | 9f44c99 | 2020-11-06 14:48:46 -0500 | [diff] [blame] | 7 | #include <filesystem> |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 8 | #include <fstream> |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 9 | #include <regex> |
| 10 | |
| 11 | namespace openpower |
| 12 | { |
| 13 | namespace software |
| 14 | { |
| 15 | namespace image |
| 16 | { |
| 17 | |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 18 | using namespace phosphor::logging; |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 19 | using AssociationList = |
| 20 | std::vector<std::tuple<std::string, std::string, std::string>>; |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 21 | |
| 22 | int 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 | |
| 54 | void MinimumShipLevel::parse(const std::string& versionStr, Version& version) |
| 55 | { |
| 56 | std::smatch match; |
| 57 | version = {0, 0, 0}; |
| 58 | |
John Wang | b41a57d | 2019-09-16 15:40:21 +0800 | [diff] [blame] | 59 | // 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 Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 61 | |
| 62 | if (!std::regex_search(versionStr, match, regex)) |
| 63 | { |
John Wang | b41a57d | 2019-09-16 15:40:21 +0800 | [diff] [blame] | 64 | // Match for vX.Y or v-X.Y |
| 65 | std::regex regexShort{"v-?([0-9]+)\\.([0-9]+)", std::regex::extended}; |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 66 | 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 | |
| 82 | std::string MinimumShipLevel::getFunctionalVersion() |
| 83 | { |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 84 | auto bus = sdbusplus::bus::new_default(); |
| 85 | auto method = bus.new_method_call(BUSNAME_UPDATER, SOFTWARE_OBJPATH, |
| 86 | SYSTEMD_PROPERTY_INTERFACE, "Get"); |
John Wang | d05d472 | 2019-09-11 15:18:15 +0800 | [diff] [blame] | 87 | method.append(ASSOCIATIONS_INTERFACE, "Associations"); |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 88 | auto response = bus.call(method); |
| 89 | |
Patrick Williams | 212102e | 2020-05-13 17:50:50 -0500 | [diff] [blame] | 90 | std::variant<AssociationList> associations; |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 91 | try |
| 92 | { |
| 93 | response.read(associations); |
| 94 | } |
Patrick Williams | 7b5685d | 2021-09-02 09:32:14 -0500 | [diff] [blame] | 95 | catch (const sdbusplus::exception::exception& e) |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 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 Williams | 550f31b | 2020-05-13 11:15:24 -0500 | [diff] [blame] | 103 | auto& assocs = std::get<AssociationList>(associations); |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 104 | if (assocs.empty()) |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 105 | { |
| 106 | return {}; |
| 107 | } |
| 108 | |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 109 | for (const auto& assoc : assocs) |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 110 | { |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 111 | 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 Williams | 212102e | 2020-05-13 17:50:50 -0500 | [diff] [blame] | 119 | std::variant<std::string> functionalVersion; |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 120 | try |
| 121 | { |
| 122 | response.read(functionalVersion); |
Patrick Williams | 550f31b | 2020-05-13 11:15:24 -0500 | [diff] [blame] | 123 | return std::get<std::string>(functionalVersion); |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 124 | } |
Patrick Williams | 7b5685d | 2021-09-02 09:32:14 -0500 | [diff] [blame] | 125 | catch (const sdbusplus::exception::exception& e) |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 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 Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 134 | } |
| 135 | |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 136 | return {}; |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 137 | } |
| 138 | |
| 139 | bool 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 Kobylak | 5c33b4c | 2018-10-17 20:16:19 -0500 | [diff] [blame] | 152 | // 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 Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 158 | |
Adriana Kobylak | 5c33b4c | 2018-10-17 20:16:19 -0500 | [diff] [blame] | 159 | // 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 Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 165 | Version actualVersion = {0, 0, 0}; |
| 166 | parse(actual, actualVersion); |
Adriana Kobylak | 5c33b4c | 2018-10-17 20:16:19 -0500 | [diff] [blame] | 167 | Version actualRev = {0, 0, actualVersion.rev}; |
| 168 | actualVersion.rev = 0; |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 169 | |
Adriana Kobylak | 5c33b4c | 2018-10-17 20:16:19 -0500 | [diff] [blame] | 170 | 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 Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 194 | if (rc < 0) |
| 195 | { |
| 196 | log<level::ERR>( |
| 197 | "PNOR Mininum Ship Level NOT met", |
Adriana Kobylak | 5c33b4c | 2018-10-17 20:16:19 -0500 | [diff] [blame] | 198 | entry("MIN_VERSION=%s", tmpMin.c_str()), |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 199 | 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 |