xref: /openbmc/phosphor-bmc-code-mgmt/common/src/software.cpp (revision bd5081f0b99a27dde8101576524b2c02a4a580b6)
1 #include "software.hpp"
2 
3 #include "device.hpp"
4 #include "software_update.hpp"
5 
6 #include <phosphor-logging/lg2.hpp>
7 #include <sdbusplus/async/context.hpp>
8 #include <xyz/openbmc_project/Association/Definitions/server.hpp>
9 #include <xyz/openbmc_project/Software/Activation/aserver.hpp>
10 #include <xyz/openbmc_project/Software/Update/aserver.hpp>
11 #include <xyz/openbmc_project/State/Host/client.hpp>
12 
13 PHOSPHOR_LOG2_USING;
14 
15 using namespace phosphor::software;
16 using namespace phosphor::software::device;
17 using namespace phosphor::software::config;
18 using namespace phosphor::software::update;
19 
20 const static std::string baseObjPathSoftware = "/xyz/openbmc_project/software/";
21 
22 SoftwareActivationProgress::SoftwareActivationProgress(
23     sdbusplus::async::context& ctx, const char* objPath) :
24     ActivationProgress(ctx, objPath)
25 {
26     // This prevents "Conditional jump or move depends on uninitialised
27     // value(s)"
28     // when properties are updated for the first time
29     progress_ = 0;
30 }
31 
32 void SoftwareActivationProgress::setProgress(int progressArg)
33 {
34     progress(progressArg);
35 }
36 
37 Software::Software(sdbusplus::async::context& ctx, Device& parent) :
38     Software(ctx, parent, getRandomSoftwareId(parent))
39 {}
40 
41 Software::Software(sdbusplus::async::context& ctx, Device& parent,
42                    const std::string& swid) :
43     SoftwareActivation(ctx, (baseObjPathSoftware + swid).c_str()),
44     objectPath(baseObjPathSoftware + swid), parentDevice(parent), swid(swid),
45     ctx(ctx)
46 {
47     // initialize the members of our base class to prevent
48     // "Conditional jump or move depends on uninitialised value(s)"
49     activation_ = Activations::NotReady;
50     requested_activation_ = RequestedActivations::None;
51 
52     std::string objPath = baseObjPathSoftware + swid;
53 
54     debug("{SWID}: created dbus interfaces on path {OBJPATH}", "SWID", swid,
55           "OBJPATH", objPath);
56 };
57 
58 long int Software::getRandomId()
59 {
60     struct timespec ts;
61     clock_gettime(CLOCK_REALTIME, &ts);
62     unsigned int seed = ts.tv_nsec ^ getpid();
63     srandom(seed);
64     return random() % 10000;
65 }
66 
67 std::string Software::getRandomSoftwareId(Device& parent)
68 {
69     return std::format("{}_{}", parent.config.configName, getRandomId());
70 }
71 
72 sdbusplus::async::task<> Software::createInventoryAssociations(bool isRunning)
73 {
74     debug("{SWID}: setting association definitions", "SWID", swid);
75 
76     std::string endpoint = "";
77 
78     try
79     {
80         endpoint = co_await parentDevice.config.getInventoryItemObjectPath(ctx);
81     }
82     catch (std::exception& e)
83     {
84         error(e.what());
85     }
86 
87     if (!associationDefinitions)
88     {
89         std::string path = objectPath;
90         associationDefinitions =
91             std::make_unique<SoftwareAssociationDefinitions>(ctx, path.c_str());
92     }
93 
94     std::vector<std::tuple<std::string, std::string, std::string>> assocs;
95 
96     if (endpoint.empty())
97     {
98         associationDefinitions->associations(assocs);
99         co_return;
100     }
101 
102     if (isRunning)
103     {
104         debug("{SWID}: creating 'running' association to {OBJPATH}", "SWID",
105               swid, "OBJPATH", endpoint);
106         std::tuple<std::string, std::string, std::string> assocRunning = {
107             "running", "ran_on", endpoint};
108         assocs.push_back(assocRunning);
109     }
110     else
111     {
112         debug("{SWID}: creating 'activating' association to {OBJPATH}", "SWID",
113               swid, "OBJPATH", endpoint);
114         std::tuple<std::string, std::string, std::string> assocActivating = {
115             "activating", "activated_on", endpoint};
116         assocs.push_back(assocActivating);
117     }
118 
119     associationDefinitions->associations(assocs);
120 
121     co_return;
122 }
123 
124 void Software::setVersion(const std::string& versionStr,
125                           SoftwareVersion::VersionPurpose versionPurpose)
126 {
127     debug("{SWID}: set version {VERSION}", "SWID", swid, "VERSION", versionStr);
128 
129     const bool emitSignal = !version;
130 
131     if (!version)
132     {
133         version =
134             std::make_unique<SoftwareVersion>(ctx, objectPath.str.c_str());
135     }
136 
137     version->version(versionStr);
138     version->purpose(versionPurpose);
139 
140     if (emitSignal)
141     {
142         version->emit_added();
143     }
144 }
145 
146 SoftwareVersion::VersionPurpose Software::getPurpose()
147 {
148     return version->purpose();
149 }
150 
151 void Software::setActivationBlocksTransition(bool enabled)
152 {
153     if (!enabled)
154     {
155         activationBlocksTransition = nullptr;
156         return;
157     }
158 
159     std::string path = objectPath;
160     activationBlocksTransition =
161         std::make_unique<SoftwareActivationBlocksTransition>(ctx, path.c_str());
162 }
163 
164 void Software::setActivation(SoftwareActivation::Activations act)
165 {
166     activation(act);
167 }
168 
169 void Software::enableUpdate(
170     const std::set<RequestedApplyTimes>& allowedApplyTimes)
171 {
172     if (updateIntf != nullptr)
173     {
174         error("[Software] update of {OBJPATH} has already been enabled",
175               "OBJPATH", objectPath);
176         return;
177     }
178 
179     debug(
180         "[Software] enabling update of {OBJPATH} (adding the update interface)",
181         "OBJPATH", objectPath);
182 
183     updateIntf = std::make_unique<SoftwareUpdate>(ctx, objectPath.str.c_str(),
184                                                   *this, allowedApplyTimes);
185 }
186