blob: b32eba9c162041decb6768c499edd26c89a7c0f9 [file] [log] [blame]
Matthew Barth11547c92020-05-05 10:34:27 -05001/**
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 Barth1826c732020-08-28 08:40:59 -050020#include <fmt/format.h>
21
Matthew Barth11547c92020-05-05 10:34:27 -050022#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
30namespace phosphor::fan
31{
32
33namespace fs = std::filesystem;
34using json = nlohmann::json;
35using namespace phosphor::logging;
36
37constexpr auto confOverridePath = "/etc/phosphor-fan-presence";
38constexpr auto confBasePath = "/usr/share/phosphor-fan-presence";
Matthew Barthfcbdc0e2020-10-28 14:11:07 -050039constexpr auto confCompatIntf =
40 "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
41constexpr auto confCompatProp = "Names";
Matthew Barth11547c92020-05-05 10:34:27 -050042
43class 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 Barthfcbdc0e2020-10-28 14:11:07 -050051 * 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 Barth11547c92020-05-05 10:34:27 -050056 * 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 Barthb5991022020-08-05 11:08:50 -050063 * @param[in] isOptional - Config file is optional, default to 'false'
Matthew Barth11547c92020-05-05 10:34:27 -050064 *
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 Barthb5991022020-08-05 11:08:50 -050070 const std::string& fileName,
71 bool isOptional = false)
Matthew Barth11547c92020-05-05 10:34:27 -050072 {
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 Barthfcbdc0e2020-10-28 14:11:07 -050080 // 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 Barth11547c92020-05-05 10:34:27 -050088 {
Matthew Barthfcbdc0e2020-10-28 14:11:07 -050089 try
Matthew Barth11547c92020-05-05 10:34:27 -050090 {
Matthew Barthfcbdc0e2020-10-28 14:11:07 -050091 // 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 Barth11547c92020-05-05 10:34:27 -0500109 }
Matthew Barthfcbdc0e2020-10-28 14:11:07 -0500110 catch (const util::DBusError&)
111 {
112 // Property unavailable on object.
113 // Set to default base path and continue to check next object
114 }
Matthew Barth11547c92020-05-05 10:34:27 -0500115 confFile = fs::path{confBasePath} / appName / fileName;
116 }
117
Matthew Bartha916f032020-08-17 10:51:48 -0500118 if (!fs::exists(confFile))
Matthew Barth11547c92020-05-05 10:34:27 -0500119 {
Matthew Bartha916f032020-08-17 10:51:48 -0500120 if (!isOptional)
121 {
Matthew Barth1826c732020-08-28 08:40:59 -0500122 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 Bartha916f032020-08-17 10:51:48 -0500130 }
131 else
132 {
133 confFile.clear();
134 }
Matthew Barthb5991022020-08-05 11:08:50 -0500135 }
Matthew Barth11547c92020-05-05 10:34:27 -0500136
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 Barthb5991022020-08-05 11:08:50 -0500153 if (!confFile.empty() && fs::exists(confFile))
Matthew Barth11547c92020-05-05 10:34:27 -0500154 {
Matthew Barth1826c732020-08-28 08:40:59 -0500155 log<level::INFO>(
156 fmt::format("Loading configuration from {}", confFile.string())
157 .c_str());
Matthew Barth11547c92020-05-05 10:34:27 -0500158 file.open(confFile);
159 try
160 {
161 jsonConf = json::parse(file);
162 }
163 catch (std::exception& e)
164 {
Matthew Barth1826c732020-08-28 08:40:59 -0500165 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 Barth11547c92020-05-05 10:34:27 -0500175 }
176 }
177 else
178 {
Matthew Barth1826c732020-08-28 08:40:59 -0500179 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 Barth11547c92020-05-05 10:34:27 -0500186 }
187
188 return jsonConf;
189 }
190};
191
192} // namespace phosphor::fan