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