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(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 sdbusplus::message::object_path obj_path; 61 bus.call(method).read(obj_path); 62 job = std::move(obj_path); 63 std::fprintf(stderr, "Triggered %s mode %s: %s\n", 64 triggerService.c_str(), mode.c_str(), job->c_str()); 65 currentStatus = ActionStatus::running; 66 return true; 67 } 68 catch (const std::exception& e) 69 { 70 job = std::nullopt; 71 jobMonitor = std::nullopt; 72 currentStatus = ActionStatus::failed; 73 std::fprintf(stderr, "Failed to trigger %s mode %s: %s\n", 74 triggerService.c_str(), mode.c_str(), e.what()); 75 return false; 76 } 77 } 78 79 void SystemdNoFile::abort() 80 { 81 if (!job) 82 { 83 return; 84 } 85 86 // Cancel the job 87 auto cancel_req = bus.new_method_call(systemdService, job->c_str(), 88 jobInterface, "Cancel"); 89 try 90 { 91 bus.call_noreply(cancel_req); 92 std::fprintf(stderr, "Canceled %s: %s\n", triggerService.c_str(), 93 job->c_str()); 94 } 95 catch (const sdbusplus::exception_t& ex) 96 { 97 std::fprintf(stderr, "Failed to cancel job %s %s: %s\n", 98 triggerService.c_str(), job->c_str(), ex.what()); 99 } 100 } 101 102 ActionStatus SystemdNoFile::status() 103 { 104 return currentStatus; 105 } 106 107 const std::string& SystemdNoFile::getMode() const 108 { 109 return mode; 110 } 111 112 void SystemdNoFile::match(sdbusplus::message_t& m) 113 { 114 if (!job) 115 { 116 std::fprintf(stderr, "No running job %s\n", triggerService.c_str()); 117 return; 118 } 119 120 uint32_t job_id; 121 sdbusplus::message::object_path job_path; 122 std::string unit; 123 std::string result; 124 try 125 { 126 m.read(job_id, job_path, unit, result); 127 } 128 catch (const sdbusplus::exception_t& e) 129 { 130 std::fprintf(stderr, "Bad JobRemoved signal %s: %s\n", 131 triggerService.c_str(), e.what()); 132 return; 133 } 134 135 if (*job != job_path.str) 136 { 137 return; 138 } 139 140 std::fprintf(stderr, "Job Finished %s %s: %s\n", triggerService.c_str(), 141 job->c_str(), result.c_str()); 142 jobMonitor = std::nullopt; 143 job = std::nullopt; 144 currentStatus = 145 result == "done" ? ActionStatus::success : ActionStatus::failed; 146 147 if (cb) 148 { 149 cb(*this); 150 } 151 } 152 153 std::unique_ptr<TriggerableActionInterface> SystemdNoFile::CreateSystemdNoFile( 154 sdbusplus::bus_t&& bus, const std::string& service, const std::string& mode) 155 { 156 return std::make_unique<SystemdNoFile>(std::move(bus), service, mode); 157 } 158 159 std::unique_ptr<TriggerableActionInterface> 160 SystemdWithStatusFile::CreateSystemdWithStatusFile( 161 sdbusplus::bus_t&& bus, const std::string& path, 162 const std::string& service, const std::string& mode) 163 { 164 return std::make_unique<SystemdWithStatusFile>(std::move(bus), path, 165 service, mode); 166 } 167 168 bool SystemdWithStatusFile::trigger() 169 { 170 if (SystemdNoFile::status() != ActionStatus::running) 171 { 172 try 173 { 174 std::ofstream ofs; 175 ofs.open(checkPath); 176 ofs << "unknown"; 177 } 178 catch (const std::exception& e) 179 { 180 return false; 181 } 182 } 183 return SystemdNoFile::trigger(); 184 } 185 186 ActionStatus SystemdWithStatusFile::status() 187 { 188 // Assume a status based on job execution if there is no file 189 ActionStatus result = SystemdNoFile::status() == ActionStatus::running 190 ? ActionStatus::running 191 : ActionStatus::failed; 192 193 std::ifstream ifs; 194 ifs.open(checkPath); 195 if (ifs.good()) 196 { 197 /* 198 * Check for the contents of the file, accepting: 199 * running, success, or failed. 200 */ 201 std::string status; 202 ifs >> status; 203 if (status == "running") 204 { 205 result = ActionStatus::running; 206 } 207 else if (status == "success") 208 { 209 result = ActionStatus::success; 210 } 211 else if (status == "failed") 212 { 213 result = ActionStatus::failed; 214 } 215 else 216 { 217 result = ActionStatus::unknown; 218 } 219 } 220 221 return result; 222 } 223 224 } // namespace ipmi_flash 225