Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 1 | #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 | |
| 10 | namespace openpower |
| 11 | { |
| 12 | namespace software |
| 13 | { |
| 14 | namespace image |
| 15 | { |
| 16 | |
| 17 | namespace fs = std::experimental::filesystem; |
William A. Kennington III | af07acf | 2018-11-06 15:15:28 -0800 | [diff] [blame] | 18 | namespace variant_ns = sdbusplus::message::variant_ns; |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 19 | using namespace phosphor::logging; |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 20 | using AssociationList = |
| 21 | std::vector<std::tuple<std::string, std::string, std::string>>; |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 22 | |
| 23 | int 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 | |
| 55 | void 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 | |
| 83 | std::string MinimumShipLevel::getFunctionalVersion() |
| 84 | { |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 85 | 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 III | af07acf | 2018-11-06 15:15:28 -0800 | [diff] [blame] | 104 | auto& assocs = variant_ns::get<AssociationList>(associations); |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 105 | if (assocs.empty()) |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 106 | { |
| 107 | return {}; |
| 108 | } |
| 109 | |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 110 | for (const auto& assoc : assocs) |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 111 | { |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 112 | 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 III | af07acf | 2018-11-06 15:15:28 -0800 | [diff] [blame] | 124 | return variant_ns::get<std::string>(functionalVersion); |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 125 | } |
| 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 Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 135 | } |
| 136 | |
Adriana Kobylak | ea4e6ae | 2018-10-30 12:17:31 -0500 | [diff] [blame] | 137 | return {}; |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 138 | } |
| 139 | |
| 140 | bool 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 Kobylak | 5c33b4c | 2018-10-17 20:16:19 -0500 | [diff] [blame] | 153 | // 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 Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 159 | |
Adriana Kobylak | 5c33b4c | 2018-10-17 20:16:19 -0500 | [diff] [blame] | 160 | // 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 Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 166 | Version actualVersion = {0, 0, 0}; |
| 167 | parse(actual, actualVersion); |
Adriana Kobylak | 5c33b4c | 2018-10-17 20:16:19 -0500 | [diff] [blame] | 168 | Version actualRev = {0, 0, actualVersion.rev}; |
| 169 | actualVersion.rev = 0; |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 170 | |
Adriana Kobylak | 5c33b4c | 2018-10-17 20:16:19 -0500 | [diff] [blame] | 171 | 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 Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 195 | if (rc < 0) |
| 196 | { |
| 197 | log<level::ERR>( |
| 198 | "PNOR Mininum Ship Level NOT met", |
Adriana Kobylak | 5c33b4c | 2018-10-17 20:16:19 -0500 | [diff] [blame] | 199 | entry("MIN_VERSION=%s", tmpMin.c_str()), |
Adriana Kobylak | 4772a94 | 2018-10-09 15:26:44 -0500 | [diff] [blame] | 200 | 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 |