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 
17 #include "internal_interface.hpp"
18 
19 #include <sdbusplus/message.hpp>
20 
21 namespace phosphor
22 {
23 namespace led
24 {
25 namespace sysfs
26 {
27 namespace interface
28 {
29 
30 InternalInterface::InternalInterface(sdbusplus::bus_t& bus, const char* path) :
31     bus(bus), serverInterface(bus, path, internalInterface, vtable.data(), this)
32 {}
33 
34 void InternalInterface::getLedDescr(const std::string& name, LedDescr& ledDescr)
35 {
36     std::vector<std::string> words;
37     boost::split(words, name, boost::is_any_of(":"));
38     try
39     {
40         ledDescr.devicename = words.at(0);
41         ledDescr.color = words.at(1);
42         ledDescr.function = words.at(2);
43     }
44     catch (const std::out_of_range& e)
45     {
46         lg2::warning("LED description {DESC} not well formed, error {ERR}",
47                      "DESC", name, "ERR", e.what());
48         throw e;
49     }
50 }
51 
52 std::string InternalInterface::getDbusName(const LedDescr& ledDescr)
53 {
54     std::vector<std::string> words;
55     words.emplace_back(ledDescr.devicename);
56     if (!ledDescr.function.empty())
57     {
58         words.emplace_back(ledDescr.function);
59     }
60 
61     if (!ledDescr.color.empty())
62     {
63         words.emplace_back(ledDescr.color);
64     }
65 
66     std::string s = boost::join(words, "_");
67 
68     sdbusplus::message::object_path path(s);
69 
70     return path.str;
71 }
72 
73 void InternalInterface::createLEDPath(const std::string& ledName)
74 {
75     std::string name;
76     std::string path = devParent + ledName;
77 
78     if (!std::filesystem::exists(fs::path(path)))
79     {
80         lg2::error("No such directory {PATH}", "PATH", path);
81         return;
82     }
83 
84     // Convert LED name in sysfs into DBus name
85     LedDescr ledDescr;
86     try
87     {
88         getLedDescr(ledName, ledDescr);
89     }
90     catch (...)
91     {
92         // Ignore the error, for simple LED which was not added in 3-part form.
93         // The simple LED can appear with it's plain name
94     }
95     name = getDbusName(ledDescr);
96 
97     lg2::debug("LED {NAME} receives dbus name {DBUSNAME}", "NAME", ledName,
98                "DBUSNAME", name);
99 
100     // Unique path name representing a single LED.
101     sdbusplus::message::object_path objPath = std::string(physParent);
102     objPath /= name;
103 
104     if (leds.contains(objPath))
105     {
106         return;
107     }
108 
109     auto sled = std::make_unique<phosphor::led::SysfsLed>(fs::path(path));
110 
111     leds.emplace(objPath, std::make_unique<phosphor::led::Physical>(
112                               bus, objPath, std::move(sled), ledDescr.color));
113 }
114 
115 void InternalInterface::addLED(const std::string& name)
116 {
117     createLEDPath(name);
118 }
119 
120 // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
121 void InternalInterface::removeLED(const std::string& name)
122 {
123     lg2::debug("RemoveLED is not configured {NAME}", "NAME", name);
124 }
125 
126 int InternalInterface::addLedConfigure(sd_bus_message* msg, void* context,
127                                        sd_bus_error* error)
128 {
129     if (msg == nullptr && context == nullptr)
130     {
131         lg2::error("Unable to configure addLed");
132         return -EINVAL;
133     }
134 
135     try
136     {
137         auto message = sdbusplus::message_t(msg);
138         auto ledName = message.unpack<std::string>();
139 
140         auto* self = static_cast<InternalInterface*>(context);
141         self->addLED(ledName);
142 
143         auto reply = message.new_method_return();
144         reply.method_return();
145     }
146     catch (const sdbusplus::exception_t& e)
147     {
148         return sd_bus_error_set(error, e.name(), e.description());
149     }
150 
151     return 1;
152 }
153 
154 int InternalInterface::removeLedConfigure(sd_bus_message* msg, void* context,
155                                           sd_bus_error* error)
156 {
157     if (msg == nullptr && context == nullptr)
158     {
159         lg2::error("Unable to configure removeLed");
160         return -EINVAL;
161     }
162 
163     try
164     {
165         auto message = sdbusplus::message_t(msg);
166         auto ledName = message.unpack<std::string>();
167 
168         auto* self = static_cast<InternalInterface*>(context);
169         self->removeLED(ledName);
170 
171         auto reply = message.new_method_return();
172         reply.method_return();
173     }
174     catch (const sdbusplus::exception_t& e)
175     {
176         return sd_bus_error_set(error, e.name(), e.description());
177     }
178 
179     return 1;
180 }
181 
182 const std::array<sdbusplus::vtable::vtable_t, 4> InternalInterface::vtable = {
183     sdbusplus::vtable::start(),
184     // AddLed method takes a string parameter and returns void
185     sdbusplus::vtable::method("AddLED", "s", "", addLedConfigure),
186     // RemoveLed method takes a string parameter and returns void
187     sdbusplus::vtable::method("RemoveLED", "s", "", removeLedConfigure),
188     sdbusplus::vtable::end()};
189 
190 } // namespace interface
191 } // namespace sysfs
192 } // namespace led
193 } // namespace phosphor
194