xref: /openbmc/phosphor-ipmi-flash/bmc/general_systemd.cpp (revision 50e505a7ba93aff6a98ae1177b671bf7be2863b5)
1 /*
2  * Copyright 2019 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "general_systemd.hpp"
18 
19 #include "status.hpp"
20 
21 #include <sdbusplus/bus.hpp>
22 
23 #include <fstream>
24 #include <memory>
25 #include <string>
26 #include <vector>
27 
28 namespace ipmi_flash
29 {
30 
31 static constexpr auto systemdService = "org.freedesktop.systemd1";
32 static constexpr auto systemdRoot = "/org/freedesktop/systemd1";
33 static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
34 static constexpr auto jobInterface = "org.freedesktop.systemd1.Job";
35 
trigger()36 bool SystemdNoFile::trigger()
37 {
38     if (job)
39     {
40         std::fprintf(stderr, "Job alreading running %s: %s\n",
41                      triggerService.c_str(), job->c_str());
42         return false;
43     }
44 
45     try
46     {
47         jobMonitor.emplace(bus,
48                            "type='signal',"
49                            "sender='org.freedesktop.systemd1',"
50                            "path='/org/freedesktop/systemd1',"
51                            "interface='org.freedesktop.systemd1.Manager',"
52                            "member='JobRemoved',",
53                            [&](sdbusplus::message_t& m) { this->match(m); });
54 
55         auto method = bus.new_method_call(systemdService, systemdRoot,
56                                           systemdInterface, "StartUnit");
57         method.append(triggerService);
58         method.append(mode);
59 
60         auto obj_path =
61             bus.call(method).unpack<sdbusplus::message::object_path>();
62 
63         job = std::move(obj_path);
64         std::fprintf(stderr, "Triggered %s mode %s: %s\n",
65                      triggerService.c_str(), mode.c_str(), job->c_str());
66         currentStatus = ActionStatus::running;
67         return true;
68     }
69     catch (const std::exception& e)
70     {
71         job = std::nullopt;
72         jobMonitor = std::nullopt;
73         currentStatus = ActionStatus::failed;
74         std::fprintf(stderr, "Failed to trigger %s mode %s: %s\n",
75                      triggerService.c_str(), mode.c_str(), e.what());
76         return false;
77     }
78 }
79 
abort()80 void SystemdNoFile::abort()
81 {
82     if (!job)
83     {
84         return;
85     }
86 
87     // Cancel the job
88     auto cancel_req = bus.new_method_call(systemdService, job->c_str(),
89                                           jobInterface, "Cancel");
90     try
91     {
92         bus.call_noreply(cancel_req);
93         std::fprintf(stderr, "Canceled %s: %s\n", triggerService.c_str(),
94                      job->c_str());
95     }
96     catch (const sdbusplus::exception_t& ex)
97     {
98         std::fprintf(stderr, "Failed to cancel job %s %s: %s\n",
99                      triggerService.c_str(), job->c_str(), ex.what());
100     }
101 }
102 
status()103 ActionStatus SystemdNoFile::status()
104 {
105     return currentStatus;
106 }
107 
getMode() const108 const std::string& SystemdNoFile::getMode() const
109 {
110     return mode;
111 }
112 
match(sdbusplus::message_t & m)113 void SystemdNoFile::match(sdbusplus::message_t& m)
114 {
115     if (!job)
116     {
117         std::fprintf(stderr, "No running job %s\n", triggerService.c_str());
118         return;
119     }
120 
121     uint32_t job_id;
122     sdbusplus::message::object_path job_path;
123     std::string unit;
124     std::string result;
125     try
126     {
127         m.read(job_id, job_path, unit, result);
128     }
129     catch (const sdbusplus::exception_t& e)
130     {
131         std::fprintf(stderr, "Bad JobRemoved signal %s: %s\n",
132                      triggerService.c_str(), e.what());
133         return;
134     }
135 
136     if (*job != job_path.str)
137     {
138         return;
139     }
140 
141     std::fprintf(stderr, "Job Finished %s %s: %s\n", triggerService.c_str(),
142                  job->c_str(), result.c_str());
143     jobMonitor = std::nullopt;
144     job = std::nullopt;
145     currentStatus =
146         result == "done" ? ActionStatus::success : ActionStatus::failed;
147 
148     if (cb)
149     {
150         cb(*this);
151     }
152 }
153 
CreateSystemdNoFile(sdbusplus::bus_t && bus,const std::string & service,const std::string & mode)154 std::unique_ptr<TriggerableActionInterface> SystemdNoFile::CreateSystemdNoFile(
155     sdbusplus::bus_t&& bus, const std::string& service, const std::string& mode)
156 {
157     return std::make_unique<SystemdNoFile>(std::move(bus), service, mode);
158 }
159 
160 std::unique_ptr<TriggerableActionInterface>
CreateSystemdWithStatusFile(sdbusplus::bus_t && bus,const std::string & path,const std::string & service,const std::string & mode)161     SystemdWithStatusFile::CreateSystemdWithStatusFile(
162         sdbusplus::bus_t&& bus, const std::string& path,
163         const std::string& service, const std::string& mode)
164 {
165     return std::make_unique<SystemdWithStatusFile>(std::move(bus), path,
166                                                    service, mode);
167 }
168 
trigger()169 bool SystemdWithStatusFile::trigger()
170 {
171     if (SystemdNoFile::status() != ActionStatus::running)
172     {
173         try
174         {
175             std::ofstream ofs;
176             ofs.open(checkPath);
177             ofs << "unknown";
178         }
179         catch (const std::exception& e)
180         {
181             return false;
182         }
183     }
184     return SystemdNoFile::trigger();
185 }
186 
status()187 ActionStatus SystemdWithStatusFile::status()
188 {
189     // Assume a status based on job execution if there is no file
190     ActionStatus result = SystemdNoFile::status() == ActionStatus::running
191                               ? ActionStatus::running
192                               : ActionStatus::failed;
193 
194     std::ifstream ifs;
195     ifs.open(checkPath);
196     if (ifs.good())
197     {
198         /*
199          * Check for the contents of the file, accepting:
200          * running, success, or failed.
201          */
202         std::string status;
203         ifs >> status;
204         if (status == "running")
205         {
206             result = ActionStatus::running;
207         }
208         else if (status == "success")
209         {
210             result = ActionStatus::success;
211         }
212         else if (status == "failed")
213         {
214             result = ActionStatus::failed;
215         }
216         else
217         {
218             result = ActionStatus::unknown;
219         }
220     }
221 
222     return result;
223 }
224 
225 } // namespace ipmi_flash
226