1 /** 2 * Copyright © 2020 IBM Corporation 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 "manager.hpp" 18 19 #include "chassis.hpp" 20 #include "config_file_parser.hpp" 21 #include "exception_utils.hpp" 22 #include "journal.hpp" 23 #include "rule.hpp" 24 #include "utility.hpp" 25 26 #include <sdbusplus/bus.hpp> 27 28 #include <chrono> 29 #include <exception> 30 #include <stdexcept> 31 #include <tuple> 32 #include <utility> 33 #include <variant> 34 35 namespace phosphor::power::regulators 36 { 37 38 namespace fs = std::filesystem; 39 40 /** 41 * Standard configuration file directory. This directory is part of the 42 * firmware install image. It contains the standard version of the config file. 43 */ 44 const fs::path standardConfigFileDir{"/usr/share/phosphor-regulators"}; 45 46 /** 47 * Test configuration file directory. This directory can contain a test version 48 * of the config file. The test version will override the standard version. 49 */ 50 const fs::path testConfigFileDir{"/etc/phosphor-regulators"}; 51 52 Manager::Manager(sdbusplus::bus::bus& bus, const sdeventplus::Event& event) : 53 ManagerObject{bus, objPath, true}, bus{bus}, eventLoop{event} 54 { 55 /* Temporarily comment out until D-Bus interface is defined and available. 56 // Subscribe to interfacesAdded signal for filename property 57 std::unique_ptr<sdbusplus::server::match::match> matchPtr = 58 std::make_unique<sdbusplus::server::match::match>( 59 bus, 60 sdbusplus::bus::match::rules::interfacesAdded(sysDbusObj).c_str(), 61 std::bind(std::mem_fn(&Manager::signalHandler), this, 62 std::placeholders::_1)); 63 signals.emplace_back(std::move(matchPtr)); 64 65 // Attempt to get the filename property from dbus 66 setFileName(getFileNameDbus()); 67 */ 68 69 // Temporarily hard-code JSON config file name to first system that will use 70 // this application. Remove this when D-Bus interface is available. 71 fileName = "ibm_rainier.json"; 72 73 if (!fileName.empty()) 74 { 75 loadConfigFile(); 76 } 77 78 // Obtain dbus service name 79 bus.request_name(busName); 80 } 81 82 void Manager::configure() 83 { 84 // Verify System object exists; this means config file has been loaded 85 if (system) 86 { 87 // Configure the regulator devices in the system 88 system->configure(); 89 } 90 else 91 { 92 journal::logErr("Unable to configure regulator devices: Configuration " 93 "file not loaded"); 94 // TODO: Log error 95 } 96 97 // TODO Configuration errors that should halt poweron, 98 // throw InternalFailure exception (or similar) to 99 // fail the call(busctl) to this method 100 } 101 102 void Manager::monitor(bool enable) 103 { 104 if (enable) 105 { 106 /* Temporarily comment out until monitoring is supported. 107 Timer timer(eventLoop, std::bind(&Manager::timerExpired, this)); 108 // Set timer as a repeating 1sec timer 109 timer.restart(std::chrono::milliseconds(1000)); 110 timers.emplace_back(std::move(timer)); 111 */ 112 } 113 else 114 { 115 /* Temporarily comment out until monitoring is supported. 116 // Delete all timers to disable monitoring 117 timers.clear(); 118 */ 119 120 // Verify System object exists; this means config file has been loaded 121 if (system) 122 { 123 // Close the regulator devices in the system. Monitoring is 124 // normally disabled because the system is being powered off. The 125 // devices should be closed in case hardware is removed or replaced 126 // while the system is at standby. 127 system->closeDevices(); 128 } 129 } 130 } 131 132 void Manager::timerExpired() 133 { 134 // TODO Analyze, refresh sensor status, and 135 // collect/update telemetry for each regulator 136 } 137 138 void Manager::sighupHandler(sdeventplus::source::Signal& /*sigSrc*/, 139 const struct signalfd_siginfo* /*sigInfo*/) 140 { 141 if (!fileName.empty()) 142 { 143 loadConfigFile(); 144 } 145 } 146 147 void Manager::signalHandler(sdbusplus::message::message& msg) 148 { 149 if (msg) 150 { 151 sdbusplus::message::object_path op; 152 msg.read(op); 153 if (static_cast<const std::string&>(op) != sysDbusPath) 154 { 155 // Object path does not match the path 156 return; 157 } 158 159 // An interfacesAdded signal returns a dictionary of interface 160 // names to a dictionary of properties and their values 161 // https://dbus.freedesktop.org/doc/dbus-specification.html 162 std::map<std::string, std::map<std::string, std::variant<std::string>>> 163 intfProp; 164 msg.read(intfProp); 165 auto itIntf = intfProp.find(sysDbusIntf); 166 if (itIntf == intfProp.cend()) 167 { 168 // Interface not found on the path 169 return; 170 } 171 auto itProp = itIntf->second.find(sysDbusProp); 172 if (itProp == itIntf->second.cend()) 173 { 174 // Property not found on the interface 175 return; 176 } 177 // Set fileName and call parse json function 178 setFileName(std::get<std::string>(itProp->second)); 179 if (!fileName.empty()) 180 { 181 loadConfigFile(); 182 } 183 } 184 } 185 186 const std::string Manager::getFileNameDbus() 187 { 188 std::string fileName = ""; 189 using namespace phosphor::power::util; 190 191 try 192 { 193 // Do not log an error when service or property are not found 194 auto service = getService(sysDbusPath, sysDbusIntf, bus, false); 195 if (!service.empty()) 196 { 197 getProperty(sysDbusIntf, sysDbusProp, sysDbusPath, service, bus, 198 fileName); 199 } 200 } 201 catch (const sdbusplus::exception::SdBusError&) 202 { 203 // File name property not available on dbus 204 fileName = ""; 205 } 206 207 return fileName; 208 } 209 210 fs::path Manager::findConfigFile() 211 { 212 // First look in the test directory 213 fs::path pathName{testConfigFileDir / fileName}; 214 if (!fs::exists(pathName)) 215 { 216 // Look in the standard directory 217 pathName = standardConfigFileDir / fileName; 218 if (!fs::exists(pathName)) 219 { 220 throw std::runtime_error{"Configuration file does not exist: " + 221 pathName.string()}; 222 } 223 } 224 225 return pathName; 226 } 227 228 void Manager::loadConfigFile() 229 { 230 try 231 { 232 // Find the absolute path to the config file 233 fs::path pathName = findConfigFile(); 234 235 // Log info message in journal; config file path is important 236 journal::logInfo("Loading configuration file " + pathName.string()); 237 238 // Parse the config file 239 std::vector<std::unique_ptr<Rule>> rules{}; 240 std::vector<std::unique_ptr<Chassis>> chassis{}; 241 std::tie(rules, chassis) = config_file_parser::parse(pathName); 242 243 // Store config file information in a new System object. The old System 244 // object, if any, is automatically deleted. 245 system = std::make_unique<System>(std::move(rules), std::move(chassis)); 246 } 247 catch (const std::exception& e) 248 { 249 // Log error messages in journal 250 exception_utils::log(e); 251 journal::logErr("Unable to load configuration file"); 252 253 // TODO: Create error log entry 254 } 255 } 256 257 } // namespace phosphor::power::regulators 258