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