1 #include "version.hpp"
2 
3 #include "item_updater.hpp"
4 #include "xyz/openbmc_project/Common/error.hpp"
5 
6 #include <openssl/evp.h>
7 
8 #include <phosphor-logging/elog-errors.hpp>
9 #include <phosphor-logging/log.hpp>
10 
11 #include <fstream>
12 #include <iostream>
13 #include <sstream>
14 #include <stdexcept>
15 #include <string>
16 
17 namespace openpower
18 {
19 namespace software
20 {
21 namespace updater
22 {
23 
24 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
25 using namespace phosphor::logging;
26 using Argument = xyz::openbmc_project::Common::InvalidArgument;
27 
28 using EVP_MD_CTX_Ptr =
29     std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_free)>;
30 
31 std::string Version::getId(const std::string& version)
32 {
33 
34     if (version.empty())
35     {
36         log<level::ERR>("Error version is empty");
37         return {};
38     }
39 
40     std::array<unsigned char, EVP_MAX_MD_SIZE> digest{};
41     EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_new(), &::EVP_MD_CTX_free);
42 
43     EVP_DigestInit(ctx.get(), EVP_sha512());
44     EVP_DigestUpdate(ctx.get(), version.c_str(), strlen(version.c_str()));
45     EVP_DigestFinal(ctx.get(), digest.data(), nullptr);
46 
47     // We are only using the first 8 characters.
48     char mdString[9];
49     snprintf(mdString, sizeof(mdString), "%02x%02x%02x%02x",
50              (unsigned int)digest[0], (unsigned int)digest[1],
51              (unsigned int)digest[2], (unsigned int)digest[3]);
52 
53     return mdString;
54 }
55 
56 std::map<std::string, std::string>
57     Version::getValue(const std::string& filePath,
58                       std::map<std::string, std::string> keys)
59 {
60     if (filePath.empty())
61     {
62         log<level::ERR>("Error filePath is empty");
63         elog<InvalidArgument>(Argument::ARGUMENT_NAME("FilePath"),
64                               Argument::ARGUMENT_VALUE(filePath.c_str()));
65     }
66 
67     std::ifstream efile;
68     std::string line;
69     efile.exceptions(std::ifstream::failbit | std::ifstream::badbit |
70                      std::ifstream::eofbit);
71 
72     try
73     {
74         efile.open(filePath);
75         while (getline(efile, line))
76         {
77             for (auto& key : keys)
78             {
79                 auto value = key.first + "=";
80                 auto keySize = value.length();
81                 if (line.compare(0, keySize, value) == 0)
82                 {
83                     key.second = line.substr(keySize);
84                     break;
85                 }
86             }
87         }
88         efile.close();
89     }
90     catch (const std::exception& e)
91     {
92         if (!efile.eof())
93         {
94             log<level::ERR>("Error in reading file");
95         }
96         efile.close();
97     }
98 
99     return keys;
100 }
101 
102 std::pair<std::string, std::string>
103     Version::getVersions(const std::string& versionPart)
104 {
105     // versionPart contains strings like below:
106     // open-power-romulus-v2.2-rc1-48-g268344f-dirty
107     //     buildroot-2018.11.1-7-g5d7cc8c
108     //     skiboot-v6.2
109     std::istringstream iss(versionPart);
110     std::string line;
111     std::string version;
112     std::stringstream ss;
113     std::string extendedVersion;
114 
115     if (!std::getline(iss, line))
116     {
117         log<level::ERR>("Unable to read from version",
118                         entry("VERSION=%s", versionPart.c_str()));
119         return {};
120     }
121     version = line;
122 
123     while (std::getline(iss, line))
124     {
125         // Each line starts with a tab, let's trim it
126         line.erase(line.begin(),
127                    std::find_if(line.begin(), line.end(),
128                                 [](int c) { return !std::isspace(c); }));
129         ss << line << ',';
130     }
131     extendedVersion = ss.str();
132 
133     // Erase the last ',', if there is one
134     if (!extendedVersion.empty())
135     {
136         extendedVersion.pop_back();
137     }
138     return {version, extendedVersion};
139 }
140 
141 void Delete::delete_()
142 {
143     if (parent.eraseCallback)
144     {
145         parent.eraseCallback(parent.getId(parent.version()));
146     }
147 }
148 
149 void Version::updateDeleteInterface(sdbusplus::message::message& msg)
150 {
151     std::string interface, chassisState;
152     std::map<std::string, std::variant<std::string>> properties;
153 
154     msg.read(interface, properties);
155 
156     for (const auto& p : properties)
157     {
158         if (p.first == "CurrentPowerState")
159         {
160             chassisState = std::get<std::string>(p.second);
161         }
162     }
163     if (chassisState.empty())
164     {
165         // The chassis power state property did not change, return.
166         return;
167     }
168 
169     if ((parent.isVersionFunctional(this->versionId)) &&
170         (chassisState != CHASSIS_STATE_OFF))
171     {
172         if (deleteObject)
173         {
174             deleteObject.reset(nullptr);
175         }
176     }
177     else
178     {
179         if (!deleteObject)
180         {
181             deleteObject = std::make_unique<Delete>(bus, objPath, *this);
182         }
183     }
184 }
185 
186 } // namespace updater
187 } // namespace software
188 } // namespace openpower
189