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