xref: /openbmc/phosphor-bmc-code-mgmt/common/src/software.cpp (revision 994a77ff25aeb28b2cce7142d081af767f9eb542)
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 // NOLINTBEGIN(readability-static-accessed-through-instance)
73 sdbusplus::async::task<> Software::createInventoryAssociations(bool isRunning)
74 // NOLINTEND(readability-static-accessed-through-instance)
75 {
76     debug("{SWID}: setting association definitions", "SWID", swid);
77 
78     std::string endpoint = "";
79 
80     try
81     {
82         endpoint = co_await parentDevice.config.getInventoryItemObjectPath(ctx);
83     }
84     catch (std::exception& e)
85     {
86         error(e.what());
87     }
88 
89     if (!associationDefinitions)
90     {
91         std::string path = objectPath;
92         associationDefinitions =
93             std::make_unique<SoftwareAssociationDefinitions>(ctx, path.c_str());
94     }
95 
96     std::vector<std::tuple<std::string, std::string, std::string>> assocs;
97 
98     if (endpoint.empty())
99     {
100         associationDefinitions->associations(assocs);
101         co_return;
102     }
103 
104     if (isRunning)
105     {
106         debug("{SWID}: creating 'running' association to {OBJPATH}", "SWID",
107               swid, "OBJPATH", endpoint);
108         std::tuple<std::string, std::string, std::string> assocRunning = {
109             "running", "ran_on", endpoint};
110         assocs.push_back(assocRunning);
111     }
112     else
113     {
114         debug("{SWID}: creating 'activating' association to {OBJPATH}", "SWID",
115               swid, "OBJPATH", endpoint);
116         std::tuple<std::string, std::string, std::string> assocActivating = {
117             "activating", "activated_on", endpoint};
118         assocs.push_back(assocActivating);
119     }
120 
121     associationDefinitions->associations(assocs);
122 
123     co_return;
124 }
125 
126 void Software::setVersion(const std::string& versionStr,
127                           SoftwareVersion::VersionPurpose versionPurpose)
128 {
129     debug("{SWID}: set version {VERSION}", "SWID", swid, "VERSION", versionStr);
130 
131     const bool emitSignal = !version;
132 
133     if (!version)
134     {
135         version =
136             std::make_unique<SoftwareVersion>(ctx, objectPath.str.c_str());
137     }
138 
139     version->version(versionStr);
140     version->purpose(versionPurpose);
141 
142     if (emitSignal)
143     {
144         version->emit_added();
145     }
146 }
147 
148 SoftwareVersion::VersionPurpose Software::getPurpose()
149 {
150     return version->purpose();
151 }
152 
153 void Software::setActivationBlocksTransition(bool enabled)
154 {
155     if (!enabled)
156     {
157         activationBlocksTransition = nullptr;
158         return;
159     }
160 
161     std::string path = objectPath;
162     activationBlocksTransition =
163         std::make_unique<SoftwareActivationBlocksTransition>(ctx, path.c_str());
164 }
165 
166 void Software::setActivation(SoftwareActivation::Activations act)
167 {
168     activation(act);
169 }
170 
171 void Software::enableUpdate(
172     const std::set<RequestedApplyTimes>& allowedApplyTimes)
173 {
174     if (updateIntf != nullptr)
175     {
176         error("[Software] update of {OBJPATH} has already been enabled",
177               "OBJPATH", objectPath);
178         return;
179     }
180 
181     debug(
182         "[Software] enabling update of {OBJPATH} (adding the update interface)",
183         "OBJPATH", objectPath);
184 
185     updateIntf = std::make_unique<SoftwareUpdate>(ctx, objectPath.str.c_str(),
186                                                   *this, allowedApplyTimes);
187 }
188