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;
18 using namespace phosphor::logging;
19 
20 int MinimumShipLevel::compare(const Version& a, const Version& b)
21 {
22     if (a.major < b.major)
23     {
24         return -1;
25     }
26     else if (a.major > b.major)
27     {
28         return 1;
29     }
30 
31     if (a.minor < b.minor)
32     {
33         return -1;
34     }
35     else if (a.minor > b.minor)
36     {
37         return 1;
38     }
39 
40     if (a.rev < b.rev)
41     {
42         return -1;
43     }
44     else if (a.rev > b.rev)
45     {
46         return 1;
47     }
48 
49     return 0;
50 }
51 
52 void MinimumShipLevel::parse(const std::string& versionStr, Version& version)
53 {
54     std::smatch match;
55     version = {0, 0, 0};
56 
57     // Match for vX.Y.Z
58     std::regex regex{"v([0-9]+)\\.([0-9]+)\\.([0-9]+)", std::regex::extended};
59 
60     if (!std::regex_search(versionStr, match, regex))
61     {
62         // Match for vX.Y
63         std::regex regexShort{"v([0-9]+)\\.([0-9]+)", std::regex::extended};
64         if (!std::regex_search(versionStr, match, regexShort))
65         {
66             log<level::ERR>("Unable to parse PNOR version",
67                             entry("VERSION=%s", versionStr.c_str()));
68             return;
69         }
70     }
71     else
72     {
73         // Populate Z
74         version.rev = std::stoi(match[3]);
75     }
76     version.major = std::stoi(match[1]);
77     version.minor = std::stoi(match[2]);
78 }
79 
80 std::string MinimumShipLevel::getFunctionalVersion()
81 {
82     if (!fs::exists(PNOR_RO_ACTIVE_PATH))
83     {
84         return {};
85     }
86 
87     fs::path versionPath(PNOR_RO_ACTIVE_PATH);
88     versionPath /= PNOR_VERSION_PARTITION;
89     if (!fs::is_regular_file(versionPath))
90     {
91         return {};
92     }
93 
94     std::ifstream versionFile(versionPath);
95     std::string versionStr;
96     std::getline(versionFile, versionStr);
97 
98     return versionStr;
99 }
100 
101 bool MinimumShipLevel::verify()
102 {
103     if (minShipLevel.empty())
104     {
105         return true;
106     }
107 
108     auto actual = getFunctionalVersion();
109     if (actual.empty())
110     {
111         return true;
112     }
113 
114     Version minVersion = {0, 0, 0};
115     parse(minShipLevel, minVersion);
116 
117     Version actualVersion = {0, 0, 0};
118     parse(actual, actualVersion);
119 
120     auto rc = compare(actualVersion, minVersion);
121     if (rc < 0)
122     {
123         log<level::ERR>(
124             "PNOR Mininum Ship Level NOT met",
125             entry("MIN_VERSION=%s", minShipLevel.c_str()),
126             entry("ACTUAL_VERSION=%s", actual.c_str()),
127             entry("VERSION_PURPOSE=%s",
128                   "xyz.openbmc_project.Software.Version.VersionPurpose.Host"));
129         return false;
130     }
131 
132     return true;
133 }
134 
135 } // namespace image
136 } // namespace software
137 } // namespace openpower
138