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