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