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 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( 48 bus, 49 "type='signal'," 50 "sender='org.freedesktop.systemd1'," 51 "path='/org/freedesktop/systemd1'," 52 "interface='org.freedesktop.systemd1.Manager'," 53 "member='JobRemoved',", 54 [&](sdbusplus::message::message& m) { this->match(m); }); 55 56 auto method = bus.new_method_call(systemdService, systemdRoot, 57 systemdInterface, "StartUnit"); 58 method.append(triggerService); 59 method.append(mode); 60 61 sdbusplus::message::object_path obj_path; 62 bus.call(method).read(obj_path); 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 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::exception& 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 103 ActionStatus SystemdNoFile::status() 104 { 105 return currentStatus; 106 } 107 108 const std::string& SystemdNoFile::getMode() const 109 { 110 return mode; 111 } 112 113 void SystemdNoFile::match(sdbusplus::message::message& 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::exception& 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 154 std::unique_ptr<TriggerableActionInterface> 155 SystemdNoFile::CreateSystemdNoFile(sdbusplus::bus::bus&& bus, 156 const std::string& service, 157 const std::string& mode) 158 { 159 return std::make_unique<SystemdNoFile>(std::move(bus), service, mode); 160 } 161 162 std::unique_ptr<TriggerableActionInterface> 163 SystemdWithStatusFile::CreateSystemdWithStatusFile( 164 sdbusplus::bus::bus&& bus, const std::string& path, 165 const std::string& service, const std::string& mode) 166 { 167 return std::make_unique<SystemdWithStatusFile>(std::move(bus), path, 168 service, mode); 169 } 170 171 bool SystemdWithStatusFile::trigger() 172 { 173 if (SystemdNoFile::status() != ActionStatus::running) 174 { 175 try 176 { 177 std::ofstream ofs; 178 ofs.open(checkPath); 179 ofs << "unknown"; 180 } 181 catch (const std::exception& e) 182 { 183 return false; 184 } 185 } 186 return SystemdNoFile::trigger(); 187 } 188 189 ActionStatus SystemdWithStatusFile::status() 190 { 191 // Assume a status based on job execution if there is no file 192 ActionStatus result = SystemdNoFile::status() == ActionStatus::running 193 ? ActionStatus::running 194 : ActionStatus::failed; 195 196 std::ifstream ifs; 197 ifs.open(checkPath); 198 if (ifs.good()) 199 { 200 /* 201 * Check for the contents of the file, accepting: 202 * running, success, or failed. 203 */ 204 std::string status; 205 ifs >> status; 206 if (status == "running") 207 { 208 result = ActionStatus::running; 209 } 210 else if (status == "success") 211 { 212 result = ActionStatus::success; 213 } 214 else if (status == "failed") 215 { 216 result = ActionStatus::failed; 217 } 218 else 219 { 220 result = ActionStatus::unknown; 221 } 222 } 223 224 return result; 225 } 226 227 } // namespace ipmi_flash 228