1 #include "config.h"
2 
3 #include "activation.hpp"
4 
5 #include "utils.hpp"
6 
7 #include <cassert>
8 #include <filesystem>
9 
10 namespace phosphor
11 {
12 namespace software
13 {
14 namespace updater
15 {
16 
17 constexpr auto SYSTEMD_BUSNAME = "org.freedesktop.systemd1";
18 constexpr auto SYSTEMD_PATH = "/org/freedesktop/systemd1";
19 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
20 
21 namespace fs = std::filesystem;
22 namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server;
23 
24 using SoftwareActivation = softwareServer::Activation;
25 
26 namespace internal
27 {
28 /** Construct the systemd service name */
29 std::string getUpdateService(const std::string& psuInventoryPath,
30                              const std::string& versionId)
31 {
32     fs::path imagePath(IMG_DIR);
33     imagePath /= versionId;
34 
35     // The systemd unit shall be escaped
36     std::string args = psuInventoryPath;
37     args += "\\x20";
38     args += imagePath;
39     std::replace(args.begin(), args.end(), '/', '-');
40 
41     std::string service = PSU_UPDATE_SERVICE;
42     auto p = service.find('@');
43     assert(p != std::string::npos);
44     service.insert(p + 1, args);
45     return service;
46 }
47 
48 } // namespace internal
49 auto Activation::activation(Activations value) -> Activations
50 {
51     if (value == Status::Activating)
52     {
53         startActivation();
54     }
55     else
56     {
57         // TODO
58     }
59 
60     return SoftwareActivation::activation(value);
61 }
62 
63 auto Activation::requestedActivation(RequestedActivations value)
64     -> RequestedActivations
65 {
66     if ((value == SoftwareActivation::RequestedActivations::Active) &&
67         (SoftwareActivation::requestedActivation() !=
68          SoftwareActivation::RequestedActivations::Active))
69     {
70         if ((activation() == Status::Ready) || (activation() == Status::Failed))
71         {
72             activation(Status::Activating);
73         }
74     }
75     return SoftwareActivation::requestedActivation(value);
76 }
77 
78 void Activation::unitStateChange(sdbusplus::message::message& msg)
79 {
80     uint32_t newStateID{};
81     sdbusplus::message::object_path newStateObjPath;
82     std::string newStateUnit{};
83     std::string newStateResult{};
84 
85     // Read the msg and populate each variable
86     msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
87 
88     if (newStateUnit == psuUpdateUnit)
89     {
90         if (newStateResult == "done")
91         {
92             finishActivation();
93         }
94         if (newStateResult == "failed" || newStateResult == "dependency")
95         {
96             activation(Status::Failed);
97         }
98     }
99 }
100 
101 void Activation::startActivation()
102 {
103     // TODO: for now only update one psu, future commits shall handle update
104     // multiple psus
105     auto psuPaths = utils::getPSUInventoryPath(bus);
106     if (psuPaths.empty())
107     {
108         return;
109     }
110 
111     psuUpdateUnit = internal::getUpdateService(psuPaths[0], versionId);
112 
113     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
114                                       SYSTEMD_INTERFACE, "StartUnit");
115     method.append(psuUpdateUnit, "replace");
116     bus.call_noreply(method);
117 }
118 
119 void Activation::finishActivation()
120 {
121     // TODO: delete the interfaces created by phosphor-software-manager
122     // TODO: delete the old software object
123     // TODO: create related associations
124     activation(Status::Active);
125 }
126 
127 } // namespace updater
128 } // namespace software
129 } // namespace phosphor
130