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 #pragma once 17 18 #include "sdbusplus.hpp" 19 20 #include <fmt/format.h> 21 22 #include <nlohmann/json.hpp> 23 #include <phosphor-logging/log.hpp> 24 #include <sdbusplus/bus.hpp> 25 #include <sdeventplus/source/signal.hpp> 26 27 #include <filesystem> 28 #include <fstream> 29 30 namespace phosphor::fan 31 { 32 33 namespace fs = std::filesystem; 34 using json = nlohmann::json; 35 using namespace phosphor::logging; 36 37 constexpr auto confOverridePath = "/etc/phosphor-fan-presence"; 38 constexpr auto confBasePath = "/usr/share/phosphor-fan-presence"; 39 constexpr auto confDbusPath = "/xyz/openbmc_project/inventory/system/chassis"; 40 constexpr auto confDbusIntf = 41 "xyz.openbmc_project.Inventory.Decorator.Compatible"; 42 constexpr auto confDbusProp = "Names"; 43 44 class JsonConfig 45 { 46 public: 47 /** 48 * Get the json configuration file. The first location found to contain 49 * the json config file for the given fan application is used from the 50 * following locations in order. 51 * 1.) From the confOverridePath location 52 * 2.) From config file found using a property value as a relative 53 * path extension on the base path from the dbus object where: 54 * path = Path set in confDbusPath 55 * interface = Interface set in confDbusIntf 56 * property = Property set in confDbusProp 57 * 3.) *DEFAULT* - From the confBasePath location 58 * 59 * @brief Get the configuration file to be used 60 * 61 * @param[in] bus - The dbus bus object 62 * @param[in] appName - The phosphor-fan-presence application name 63 * @param[in] fileName - Application's configuration file's name 64 * @param[in] isOptional - Config file is optional, default to 'false' 65 * 66 * @return filesystem path 67 * The filesystem path to the configuration file to use 68 */ 69 static const fs::path getConfFile(sdbusplus::bus::bus& bus, 70 const std::string& appName, 71 const std::string& fileName, 72 bool isOptional = false) 73 { 74 // Check override location 75 fs::path confFile = fs::path{confOverridePath} / appName / fileName; 76 if (fs::exists(confFile)) 77 { 78 return confFile; 79 } 80 81 try 82 { 83 // Retrieve json config relative path location from dbus 84 auto confDbusValue = 85 util::SDBusPlus::getProperty<std::vector<std::string>>( 86 bus, confDbusPath, confDbusIntf, confDbusProp); 87 // Look for a config file at each entry relative to the base 88 // path and use the first one found 89 auto it = std::find_if( 90 confDbusValue.begin(), confDbusValue.end(), 91 [&confFile, &appName, &fileName](auto const& entry) { 92 confFile = 93 fs::path{confBasePath} / appName / entry / fileName; 94 return fs::exists(confFile); 95 }); 96 if (it == confDbusValue.end()) 97 { 98 // Property exists, but no config file found. Use default base 99 // path 100 confFile = fs::path{confBasePath} / appName / fileName; 101 } 102 } 103 catch (const util::DBusError&) 104 { 105 // Property unavailable, attempt default base path 106 confFile = fs::path{confBasePath} / appName / fileName; 107 } 108 109 if (!fs::exists(confFile)) 110 { 111 if (!isOptional) 112 { 113 log<level::ERR>( 114 fmt::format("No JSON config file found. Default file: {}", 115 confFile.string()) 116 .c_str()); 117 throw std::runtime_error( 118 fmt::format("No JSON config file found. Default file: {}", 119 confFile.string()) 120 .c_str()); 121 } 122 else 123 { 124 confFile.clear(); 125 } 126 } 127 128 return confFile; 129 } 130 131 /** 132 * @brief Load the JSON config file 133 * 134 * @param[in] confFile - File system path of the configuration file to load 135 * 136 * @return Parsed JSON object 137 * The parsed JSON configuration file object 138 */ 139 static const json load(const fs::path& confFile) 140 { 141 std::ifstream file; 142 json jsonConf; 143 144 if (!confFile.empty() && fs::exists(confFile)) 145 { 146 log<level::INFO>( 147 fmt::format("Loading configuration from {}", confFile.string()) 148 .c_str()); 149 file.open(confFile); 150 try 151 { 152 jsonConf = json::parse(file); 153 } 154 catch (std::exception& e) 155 { 156 log<level::ERR>( 157 fmt::format( 158 "Failed to parse JSON config file: {}, error: {}", 159 confFile.string(), e.what()) 160 .c_str()); 161 throw std::runtime_error( 162 fmt::format( 163 "Failed to parse JSON config file: {}, error: {}", 164 confFile.string(), e.what()) 165 .c_str()); 166 } 167 } 168 else 169 { 170 log<level::ERR>(fmt::format("Unable to open JSON config file: {}", 171 confFile.string()) 172 .c_str()); 173 throw std::runtime_error( 174 fmt::format("Unable to open JSON config file: {}", 175 confFile.string()) 176 .c_str()); 177 } 178 179 return jsonConf; 180 } 181 }; 182 183 } // namespace phosphor::fan 184