1 #include "config.h"
2 
3 #include "config-validator.hpp"
4 #include "grouplayout.hpp"
5 #include "json-config.hpp"
6 #include "ledlayout.hpp"
7 
8 #include <nlohmann/json.hpp>
9 #include <phosphor-logging/lg2.hpp>
10 #include <sdbusplus/bus.hpp>
11 #include <sdeventplus/event.hpp>
12 
13 #include <filesystem>
14 #include <fstream>
15 #include <iostream>
16 
17 namespace fs = std::filesystem;
18 
19 using Json = nlohmann::json;
20 
21 // Priority for a particular LED needs to stay SAME across all groups
22 using PriorityMap =
23     std::unordered_map<std::string,
24                        std::optional<phosphor::led::Layout::Action>>;
25 
26 /** @brief Parse LED JSON file and output Json object
27  *
28  *  @param[in] path - path of LED JSON file
29  *
30  *  @return const Json - Json object
31  */
32 Json readJson(const fs::path& path)
33 {
34     if (!fs::exists(path) || fs::is_empty(path))
35     {
36         lg2::error("Incorrect File Path or empty file, FILE_PATH = {PATH}",
37                    "PATH", path);
38         throw std::runtime_error("Incorrect File Path or empty file");
39     }
40 
41     try
42     {
43         std::ifstream jsonFile(path);
44         return Json::parse(jsonFile);
45     }
46     catch (const std::exception& e)
47     {
48         lg2::error(
49             "Failed to parse config file, ERROR = {ERROR}, FILE_PATH = {PATH}",
50             "ERROR", e, "PATH", path);
51         throw std::runtime_error("Failed to parse config file");
52     }
53 }
54 
55 /** @brief Returns action enum based on string
56  *
57  *  @param[in] action - action string
58  *
59  *  @return Action - action enum (On/Off/Blink)
60  */
61 phosphor::led::Layout::Action getAction(const std::string& action)
62 {
63     if (action == "On")
64     {
65         return phosphor::led::Layout::Action::On;
66     }
67     if (action == "Off")
68     {
69         return phosphor::led::Layout::Action::Off;
70     }
71     if (action == "Blink")
72     {
73         return phosphor::led::Layout::Action::Blink;
74     }
75 
76     assert(false);
77     return phosphor::led::Layout::Action::Blink;
78 }
79 
80 static void loadJsonConfigV1GroupMember(const Json& member,
81                                         phosphor::led::ActionSet& ledActions)
82 {
83     auto name = member.value("Name", "");
84     auto action = getAction(member.value("Action", ""));
85     uint8_t dutyOn = member.value("DutyOn", 50);
86     uint16_t period = member.value("Period", 0);
87 
88     const std::string priorityStr = member.value("Priority", "");
89     std::optional<phosphor::led::Layout::Action> priority = std::nullopt;
90 
91     if (!priorityStr.empty())
92     {
93         priority = getAction(priorityStr);
94     }
95 
96     phosphor::led::Layout::LedAction ledAction{name, action, dutyOn, period,
97                                                priority};
98     ledActions.emplace(ledAction);
99 }
100 
101 static void loadJsonConfigV1Group(const Json& entry,
102                                   phosphor::led::GroupMap& ledMap)
103 {
104     const Json empty{};
105 
106     fs::path tmpPath("/xyz/openbmc_project/led/groups");
107 
108     const std::string groupName = entry.value("group", "");
109 
110     tmpPath /= groupName;
111     auto objpath = tmpPath.string();
112     auto members = entry.value("members", empty);
113 
114     lg2::debug("config for '{GROUP}'", "GROUP", groupName);
115 
116     int priority = entry.value("Priority", 0);
117 
118     phosphor::led::ActionSet ledActions{};
119     phosphor::led::Layout::GroupLayout groupLayout{};
120     for (const auto& member : members)
121     {
122         loadJsonConfigV1GroupMember(member, ledActions);
123     }
124 
125     // Generated an std::unordered_map of LedGroupNames to std::set of LEDs
126     // containing the name and properties.
127     groupLayout.actionSet = ledActions;
128     groupLayout.priority = priority;
129 
130     ledMap.emplace(objpath, groupLayout);
131 }
132 
133 /** @brief Load JSON config and return led map (JSON version 1)
134  *
135  *  @return phosphor::led::GroupMap
136  */
137 const phosphor::led::GroupMap loadJsonConfigV1(const Json& json)
138 {
139     phosphor::led::GroupMap ledMap{};
140 
141     // define the default JSON as empty
142     const Json empty{};
143     auto leds = json.value("leds", empty);
144 
145     for (const auto& entry : leds)
146     {
147         loadJsonConfigV1Group(entry, ledMap);
148     }
149 
150     return ledMap;
151 }
152 
153 /** @brief Load JSON config and return led map
154  *
155  *  @return phosphor::led::GroupMap
156  */
157 const phosphor::led::GroupMap loadJsonConfig(const fs::path& path)
158 {
159     auto json = readJson(path);
160 
161     auto version = json.value("version", 1);
162     switch (version)
163     {
164         case 1:
165             return loadJsonConfigV1(json);
166 
167         default:
168             lg2::error("Unsupported JSON Version: {VERSION}", "VERSION",
169                        version);
170             throw std::runtime_error("Unsupported version");
171     }
172 
173     return phosphor::led::GroupMap{};
174 }
175 
176 /** @brief Get led map from LED groups JSON config
177  *
178  *  @param[in] config - Path to the JSON config.
179  *  @return phosphor::led::GroupMap
180  *
181  *  @note if config is an empty string, daemon will interrogate dbus for
182  *        compatible strings.
183  */
184 const phosphor::led::GroupMap getSystemLedMap(fs::path config)
185 {
186     if (config.empty())
187     {
188         config = phosphor::led::getJsonConfig();
189     }
190 
191     return loadJsonConfig(config);
192 }
193