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 /* Temporarily comment out until monitoring is supported. 105 if (enable) 106 { 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 else 113 { 114 // Delete all timers to disable monitoring 115 timers.clear(); 116 } 117 */ 118 } 119 120 void Manager::timerExpired() 121 { 122 // TODO Analyze, refresh sensor status, and 123 // collect/update telemetry for each regulator 124 } 125 126 void Manager::sighupHandler(sdeventplus::source::Signal& /*sigSrc*/, 127 const struct signalfd_siginfo* /*sigInfo*/) 128 { 129 if (!fileName.empty()) 130 { 131 loadConfigFile(); 132 } 133 } 134 135 void Manager::signalHandler(sdbusplus::message::message& msg) 136 { 137 if (msg) 138 { 139 sdbusplus::message::object_path op; 140 msg.read(op); 141 if (static_cast<const std::string&>(op) != sysDbusPath) 142 { 143 // Object path does not match the path 144 return; 145 } 146 147 // An interfacesAdded signal returns a dictionary of interface 148 // names to a dictionary of properties and their values 149 // https://dbus.freedesktop.org/doc/dbus-specification.html 150 std::map<std::string, std::map<std::string, std::variant<std::string>>> 151 intfProp; 152 msg.read(intfProp); 153 auto itIntf = intfProp.find(sysDbusIntf); 154 if (itIntf == intfProp.cend()) 155 { 156 // Interface not found on the path 157 return; 158 } 159 auto itProp = itIntf->second.find(sysDbusProp); 160 if (itProp == itIntf->second.cend()) 161 { 162 // Property not found on the interface 163 return; 164 } 165 // Set fileName and call parse json function 166 setFileName(std::get<std::string>(itProp->second)); 167 if (!fileName.empty()) 168 { 169 loadConfigFile(); 170 } 171 } 172 } 173 174 const std::string Manager::getFileNameDbus() 175 { 176 std::string fileName = ""; 177 using namespace phosphor::power::util; 178 179 try 180 { 181 // Do not log an error when service or property are not found 182 auto service = getService(sysDbusPath, sysDbusIntf, bus, false); 183 if (!service.empty()) 184 { 185 getProperty(sysDbusIntf, sysDbusProp, sysDbusPath, service, bus, 186 fileName); 187 } 188 } 189 catch (const sdbusplus::exception::SdBusError&) 190 { 191 // File name property not available on dbus 192 fileName = ""; 193 } 194 195 return fileName; 196 } 197 198 fs::path Manager::findConfigFile() 199 { 200 // First look in the test directory 201 fs::path pathName{testConfigFileDir / fileName}; 202 if (!fs::exists(pathName)) 203 { 204 // Look in the standard directory 205 pathName = standardConfigFileDir / fileName; 206 if (!fs::exists(pathName)) 207 { 208 throw std::runtime_error{"Configuration file does not exist: " + 209 pathName.string()}; 210 } 211 } 212 213 return pathName; 214 } 215 216 void Manager::loadConfigFile() 217 { 218 try 219 { 220 // Find the absolute path to the config file 221 fs::path pathName = findConfigFile(); 222 223 // Log info message in journal; config file path is important 224 journal::logInfo("Loading configuration file " + pathName.string()); 225 226 // Parse the config file 227 std::vector<std::unique_ptr<Rule>> rules{}; 228 std::vector<std::unique_ptr<Chassis>> chassis{}; 229 std::tie(rules, chassis) = config_file_parser::parse(pathName); 230 231 // Store config file information in a new System object. The old System 232 // object, if any, is automatically deleted. 233 system = std::make_unique<System>(std::move(rules), std::move(chassis)); 234 } 235 catch (const std::exception& e) 236 { 237 // Log error messages in journal 238 exception_utils::log(e); 239 journal::logErr("Unable to load configuration file"); 240 241 // TODO: Create error log entry 242 } 243 } 244 245 } // namespace phosphor::power::regulators 246