Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 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 | |
Matthew Barth | 1826c73 | 2020-08-28 08:40:59 -0500 | [diff] [blame] | 20 | #include <fmt/format.h> |
| 21 | |
Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 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"; |
Matthew Barth | fcbdc0e | 2020-10-28 14:11:07 -0500 | [diff] [blame^] | 39 | constexpr auto confCompatIntf = |
| 40 | "xyz.openbmc_project.Configuration.IBMCompatibleSystem"; |
| 41 | constexpr auto confCompatProp = "Names"; |
Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 42 | |
| 43 | class JsonConfig |
| 44 | { |
| 45 | public: |
| 46 | /** |
| 47 | * Get the json configuration file. The first location found to contain |
| 48 | * the json config file for the given fan application is used from the |
| 49 | * following locations in order. |
| 50 | * 1.) From the confOverridePath location |
Matthew Barth | fcbdc0e | 2020-10-28 14:11:07 -0500 | [diff] [blame^] | 51 | * 2.) From config file found using an entry from a list obtained from an |
| 52 | * interface's property as a relative path extension on the base path where: |
| 53 | * interface = Interface set in confCompatIntf with the property |
| 54 | * property = Property set in confCompatProp containing a list of |
| 55 | * subdirectories in priority order to find a config |
Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 56 | * 3.) *DEFAULT* - From the confBasePath location |
| 57 | * |
| 58 | * @brief Get the configuration file to be used |
| 59 | * |
| 60 | * @param[in] bus - The dbus bus object |
| 61 | * @param[in] appName - The phosphor-fan-presence application name |
| 62 | * @param[in] fileName - Application's configuration file's name |
Matthew Barth | b599102 | 2020-08-05 11:08:50 -0500 | [diff] [blame] | 63 | * @param[in] isOptional - Config file is optional, default to 'false' |
Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 64 | * |
| 65 | * @return filesystem path |
| 66 | * The filesystem path to the configuration file to use |
| 67 | */ |
| 68 | static const fs::path getConfFile(sdbusplus::bus::bus& bus, |
| 69 | const std::string& appName, |
Matthew Barth | b599102 | 2020-08-05 11:08:50 -0500 | [diff] [blame] | 70 | const std::string& fileName, |
| 71 | bool isOptional = false) |
Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 72 | { |
| 73 | // Check override location |
| 74 | fs::path confFile = fs::path{confOverridePath} / appName / fileName; |
| 75 | if (fs::exists(confFile)) |
| 76 | { |
| 77 | return confFile; |
| 78 | } |
| 79 | |
Matthew Barth | fcbdc0e | 2020-10-28 14:11:07 -0500 | [diff] [blame^] | 80 | // Default base path used if no config file found at any locations |
| 81 | // provided on dbus objects with the compatible interface |
| 82 | confFile = fs::path{confBasePath} / appName / fileName; |
| 83 | |
| 84 | // Get all objects implementing the compatible interface |
| 85 | auto objects = |
| 86 | util::SDBusPlus::getSubTreePathsRaw(bus, "/", confCompatIntf, 0); |
| 87 | for (auto& path : objects) |
Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 88 | { |
Matthew Barth | fcbdc0e | 2020-10-28 14:11:07 -0500 | [diff] [blame^] | 89 | try |
Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 90 | { |
Matthew Barth | fcbdc0e | 2020-10-28 14:11:07 -0500 | [diff] [blame^] | 91 | // Retrieve json config compatible relative path locations |
| 92 | auto confCompatValue = |
| 93 | util::SDBusPlus::getProperty<std::vector<std::string>>( |
| 94 | bus, path, confCompatIntf, confCompatProp); |
| 95 | // Look for a config file at each entry relative to the base |
| 96 | // path and use the first one found |
| 97 | auto it = std::find_if( |
| 98 | confCompatValue.begin(), confCompatValue.end(), |
| 99 | [&confFile, &appName, &fileName](auto const& entry) { |
| 100 | confFile = |
| 101 | fs::path{confBasePath} / appName / entry / fileName; |
| 102 | return fs::exists(confFile); |
| 103 | }); |
| 104 | if (it != confCompatValue.end()) |
| 105 | { |
| 106 | // Use the first config file found at a listed location |
| 107 | break; |
| 108 | } |
Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 109 | } |
Matthew Barth | fcbdc0e | 2020-10-28 14:11:07 -0500 | [diff] [blame^] | 110 | catch (const util::DBusError&) |
| 111 | { |
| 112 | // Property unavailable on object. |
| 113 | // Set to default base path and continue to check next object |
| 114 | } |
Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 115 | confFile = fs::path{confBasePath} / appName / fileName; |
| 116 | } |
| 117 | |
Matthew Barth | a916f03 | 2020-08-17 10:51:48 -0500 | [diff] [blame] | 118 | if (!fs::exists(confFile)) |
Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 119 | { |
Matthew Barth | a916f03 | 2020-08-17 10:51:48 -0500 | [diff] [blame] | 120 | if (!isOptional) |
| 121 | { |
Matthew Barth | 1826c73 | 2020-08-28 08:40:59 -0500 | [diff] [blame] | 122 | log<level::ERR>( |
| 123 | fmt::format("No JSON config file found. Default file: {}", |
| 124 | confFile.string()) |
| 125 | .c_str()); |
| 126 | throw std::runtime_error( |
| 127 | fmt::format("No JSON config file found. Default file: {}", |
| 128 | confFile.string()) |
| 129 | .c_str()); |
Matthew Barth | a916f03 | 2020-08-17 10:51:48 -0500 | [diff] [blame] | 130 | } |
| 131 | else |
| 132 | { |
| 133 | confFile.clear(); |
| 134 | } |
Matthew Barth | b599102 | 2020-08-05 11:08:50 -0500 | [diff] [blame] | 135 | } |
Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 136 | |
| 137 | return confFile; |
| 138 | } |
| 139 | |
| 140 | /** |
| 141 | * @brief Load the JSON config file |
| 142 | * |
| 143 | * @param[in] confFile - File system path of the configuration file to load |
| 144 | * |
| 145 | * @return Parsed JSON object |
| 146 | * The parsed JSON configuration file object |
| 147 | */ |
| 148 | static const json load(const fs::path& confFile) |
| 149 | { |
| 150 | std::ifstream file; |
| 151 | json jsonConf; |
| 152 | |
Matthew Barth | b599102 | 2020-08-05 11:08:50 -0500 | [diff] [blame] | 153 | if (!confFile.empty() && fs::exists(confFile)) |
Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 154 | { |
Matthew Barth | 1826c73 | 2020-08-28 08:40:59 -0500 | [diff] [blame] | 155 | log<level::INFO>( |
| 156 | fmt::format("Loading configuration from {}", confFile.string()) |
| 157 | .c_str()); |
Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 158 | file.open(confFile); |
| 159 | try |
| 160 | { |
| 161 | jsonConf = json::parse(file); |
| 162 | } |
| 163 | catch (std::exception& e) |
| 164 | { |
Matthew Barth | 1826c73 | 2020-08-28 08:40:59 -0500 | [diff] [blame] | 165 | log<level::ERR>( |
| 166 | fmt::format( |
| 167 | "Failed to parse JSON config file: {}, error: {}", |
| 168 | confFile.string(), e.what()) |
| 169 | .c_str()); |
| 170 | throw std::runtime_error( |
| 171 | fmt::format( |
| 172 | "Failed to parse JSON config file: {}, error: {}", |
| 173 | confFile.string(), e.what()) |
| 174 | .c_str()); |
Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 175 | } |
| 176 | } |
| 177 | else |
| 178 | { |
Matthew Barth | 1826c73 | 2020-08-28 08:40:59 -0500 | [diff] [blame] | 179 | log<level::ERR>(fmt::format("Unable to open JSON config file: {}", |
| 180 | confFile.string()) |
| 181 | .c_str()); |
| 182 | throw std::runtime_error( |
| 183 | fmt::format("Unable to open JSON config file: {}", |
| 184 | confFile.string()) |
| 185 | .c_str()); |
Matthew Barth | 11547c9 | 2020-05-05 10:34:27 -0500 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | return jsonConf; |
| 189 | } |
| 190 | }; |
| 191 | |
| 192 | } // namespace phosphor::fan |