xref: /openbmc/phosphor-host-ipmid/libipmid/utils.cpp (revision 93aa983e36a7140d594e175a00a5b3a0021146b8)
1 #include <arpa/inet.h>
2 #include <dirent.h>
3 #include <fcntl.h>
4 #include <linux/i2c-dev.h>
5 #include <linux/i2c.h>
6 #include <net/if.h>
7 #include <sys/ioctl.h>
8 #include <sys/types.h>
9 #include <unistd.h>
10 
11 #include <ipmid/utils.hpp>
12 #include <phosphor-logging/elog-errors.hpp>
13 #include <phosphor-logging/lg2.hpp>
14 #include <sdbusplus/message/types.hpp>
15 #include <xyz/openbmc_project/Common/error.hpp>
16 #include <xyz/openbmc_project/ObjectMapper/common.hpp>
17 
18 #include <algorithm>
19 #include <chrono>
20 #include <sstream>
21 
22 namespace ipmi
23 {
24 
25 using namespace phosphor::logging;
26 using namespace sdbusplus::error::xyz::openbmc_project::common;
27 
28 using ObjectMapper = sdbusplus::common::xyz::openbmc_project::ObjectMapper;
29 
30 namespace network
31 {
32 
33 /** @brief checks if the given ip is Link Local Ip or not.
34  *  @param[in] ipaddress - IPAddress.
35  */
36 bool isLinkLocalIP(const std::string& ipaddress);
37 
38 } // namespace network
39 
40 // TODO There may be cases where an interface is implemented by multiple
41 //  objects,to handle such cases we are interested on that object
42 //  which are on interested busname.
43 //  Currently mapper doesn't give the readable busname(gives busid) so we can't
44 //  use busname to find the object,will do later once the support is there.
45 
getDbusObject(sdbusplus::bus_t & bus,const std::string & interface,const std::string & serviceRoot,const std::string & match)46 DbusObjectInfo getDbusObject(
47     sdbusplus::bus_t& bus, const std::string& interface,
48     const std::string& serviceRoot, const std::string& match)
49 {
50     std::vector<DbusInterface> interfaces;
51     interfaces.emplace_back(interface);
52 
53     ObjectTree objectTree = getSubTree(bus, interfaces, serviceRoot);
54     if (objectTree.empty())
55     {
56         lg2::error("No Object has implemented the interface: {INTERFACE}",
57                    "INTERFACE", interface);
58         elog<InternalFailure>();
59     }
60 
61     DbusObjectInfo objectInfo;
62 
63     // if match is empty then return the first object
64     if (match == "")
65     {
66         objectInfo = std::make_pair(
67             objectTree.begin()->first,
68             std::move(objectTree.begin()->second.begin()->first));
69         return objectInfo;
70     }
71 
72     // else search the match string in the object path
73     auto found = std::find_if(
74         objectTree.begin(), objectTree.end(), [&match](const auto& object) {
75             return (object.first.find(match) != std::string::npos);
76         });
77 
78     if (found == objectTree.end())
79     {
80         lg2::error("Failed to find object which matches: {MATCH}", "MATCH",
81                    match);
82         elog<InternalFailure>();
83         // elog<> throws an exception.
84     }
85 
86     return make_pair(found->first, std::move(found->second.begin()->first));
87 }
88 
getDbusProperty(sdbusplus::bus_t & bus,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & property,std::chrono::microseconds timeout)89 Value getDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
90                       const std::string& objPath, const std::string& interface,
91                       const std::string& property,
92                       std::chrono::microseconds timeout)
93 {
94     Value value;
95 
96     auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
97                                       PROP_INTF, METHOD_GET);
98 
99     method.append(interface, property);
100 
101     auto reply = bus.call(method, timeout.count());
102     reply.read(value);
103 
104     return value;
105 }
106 
getAllDbusProperties(sdbusplus::bus_t & bus,const std::string & service,const std::string & objPath,const std::string & interface,std::chrono::microseconds timeout)107 PropertyMap getAllDbusProperties(
108     sdbusplus::bus_t& bus, const std::string& service,
109     const std::string& objPath, const std::string& interface,
110     std::chrono::microseconds timeout)
111 {
112     PropertyMap properties;
113 
114     auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
115                                       PROP_INTF, METHOD_GET_ALL);
116 
117     method.append(interface);
118 
119     auto reply = bus.call(method, timeout.count());
120     reply.read(properties);
121 
122     return properties;
123 }
124 
getManagedObjects(sdbusplus::bus_t & bus,const std::string & service,const std::string & objPath)125 ObjectValueTree getManagedObjects(sdbusplus::bus_t& bus,
126                                   const std::string& service,
127                                   const std::string& objPath)
128 {
129     ipmi::ObjectValueTree interfaces;
130 
131     auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
132                                       "org.freedesktop.DBus.ObjectManager",
133                                       "GetManagedObjects");
134     auto reply = bus.call(method);
135     reply.read(interfaces);
136 
137     return interfaces;
138 }
139 
setDbusProperty(sdbusplus::bus_t & bus,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & property,const Value & value,std::chrono::microseconds timeout)140 void setDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
141                      const std::string& objPath, const std::string& interface,
142                      const std::string& property, const Value& value,
143                      std::chrono::microseconds timeout)
144 {
145     auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
146                                       PROP_INTF, METHOD_SET);
147 
148     method.append(interface, property, value);
149 
150     if (!bus.call(method, timeout.count()))
151     {
152         lg2::error("Failed to set {PROPERTY}, path: {PATH}, "
153                    "interface: {INTERFACE}",
154                    "PROPERTY", property, "PATH", objPath, "INTERFACE",
155                    interface);
156         elog<InternalFailure>();
157     }
158 }
159 
ServiceCache(const std::string & intf,const std::string & path)160 ServiceCache::ServiceCache(const std::string& intf, const std::string& path) :
161     intf(intf), path(path), cachedService(std::nullopt),
162     cachedBusName(std::nullopt)
163 {}
164 
ServiceCache(std::string && intf,std::string && path)165 ServiceCache::ServiceCache(std::string&& intf, std::string&& path) :
166     intf(std::move(intf)), path(std::move(path)), cachedService(std::nullopt),
167     cachedBusName(std::nullopt)
168 {}
169 
getService(sdbusplus::bus_t & bus)170 const std::string& ServiceCache::getService(sdbusplus::bus_t& bus)
171 {
172     if (!isValid(bus))
173     {
174         cachedBusName = bus.get_unique_name();
175         cachedService = ::ipmi::getService(bus, intf, path);
176     }
177 
178     if (!cachedService)
179     {
180         throw std::runtime_error("Service not cached");
181     }
182     return cachedService.value();
183 }
184 
invalidate()185 void ServiceCache::invalidate()
186 {
187     cachedBusName = std::nullopt;
188     cachedService = std::nullopt;
189 }
190 
newMethodCall(sdbusplus::bus_t & bus,const char * intf,const char * method)191 sdbusplus::message_t ServiceCache::newMethodCall(
192     sdbusplus::bus_t& bus, const char* intf, const char* method)
193 {
194     return bus.new_method_call(getService(bus).c_str(), path.c_str(), intf,
195                                method);
196 }
197 
isValid(sdbusplus::bus_t & bus) const198 bool ServiceCache::isValid(sdbusplus::bus_t& bus) const
199 {
200     return cachedService && cachedBusName == bus.get_unique_name();
201 }
202 
getService(sdbusplus::bus_t & bus,const std::string & intf,const std::string & path)203 std::string getService(sdbusplus::bus_t& bus, const std::string& intf,
204                        const std::string& path)
205 {
206     auto mapperCall = bus.new_method_call(
207         ObjectMapper::default_service, ObjectMapper::instance_path,
208         ObjectMapper::interface, ObjectMapper::method_names::get_object);
209 
210     mapperCall.append(path);
211     mapperCall.append(std::vector<std::string>({intf}));
212 
213     auto mapperResponseMsg = bus.call(mapperCall);
214 
215     auto mapperResponse =
216         mapperResponseMsg
217             .unpack<std::map<std::string, std::vector<std::string>>>();
218 
219     if (mapperResponse.begin() == mapperResponse.end())
220     {
221         throw std::runtime_error("ERROR in reading the mapper response");
222     }
223 
224     return mapperResponse.begin()->first;
225 }
226 
getSubTree(sdbusplus::bus_t & bus,const InterfaceList & interfaces,const std::string & subtreePath,int32_t depth)227 ObjectTree getSubTree(sdbusplus::bus_t& bus, const InterfaceList& interfaces,
228                       const std::string& subtreePath, int32_t depth)
229 {
230     auto mapperCall = bus.new_method_call(
231         ObjectMapper::default_service, ObjectMapper::instance_path,
232         ObjectMapper::interface, ObjectMapper::method_names::get_sub_tree);
233 
234     mapperCall.append(subtreePath, depth, interfaces);
235 
236     auto mapperReply = bus.call(mapperCall);
237     auto objectTree = mapperReply.unpack<ObjectTree>();
238 
239     return objectTree;
240 }
241 
getAllDbusObjects(sdbusplus::bus_t & bus,const std::string & serviceRoot,const std::string & interface,const std::string & match)242 ipmi::ObjectTree getAllDbusObjects(
243     sdbusplus::bus_t& bus, const std::string& serviceRoot,
244     const std::string& interface, const std::string& match)
245 {
246     std::vector<std::string> interfaces;
247     interfaces.emplace_back(interface);
248 
249     ObjectTree objectTree = getSubTree(bus, interfaces, serviceRoot);
250     for (auto it = objectTree.begin(); it != objectTree.end();)
251     {
252         if (it->first.find(match) == std::string::npos)
253         {
254             it = objectTree.erase(it);
255         }
256         else
257         {
258             ++it;
259         }
260     }
261 
262     return objectTree;
263 }
264 
265 namespace method_no_args
266 {
267 
callDbusMethod(sdbusplus::bus_t & bus,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & method)268 void callDbusMethod(sdbusplus::bus_t& bus, const std::string& service,
269                     const std::string& objPath, const std::string& interface,
270                     const std::string& method)
271 
272 {
273     auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
274                                          interface.c_str(), method.c_str());
275     auto reply = bus.call(busMethod);
276 }
277 
278 } // namespace method_no_args
279 
280 /********* Begin co-routine yielding alternatives ***************/
281 
getService(Context::ptr ctx,const std::string & intf,const std::string & path,std::string & service)282 boost::system::error_code getService(Context::ptr ctx, const std::string& intf,
283                                      const std::string& path,
284                                      std::string& service)
285 {
286     boost::system::error_code ec;
287     std::map<std::string, std::vector<std::string>> mapperResponse =
288         ctx->bus->yield_method_call<decltype(mapperResponse)>(
289             ctx->yield, ec, ObjectMapper::default_service,
290             ObjectMapper::instance_path, ObjectMapper::interface,
291             ObjectMapper::method_names::get_object, path,
292             std::vector<std::string>({intf}));
293 
294     if (!ec)
295     {
296         service = std::move(mapperResponse.begin()->first);
297     }
298     return ec;
299 }
300 
getSubTree(Context::ptr ctx,const InterfaceList & interfaces,const std::string & subtreePath,int32_t depth,ObjectTree & objectTree)301 boost::system::error_code getSubTree(
302     Context::ptr ctx, const InterfaceList& interfaces,
303     const std::string& subtreePath, int32_t depth, ObjectTree& objectTree)
304 {
305     boost::system::error_code ec;
306     objectTree = ctx->bus->yield_method_call<ObjectTree>(
307         ctx->yield, ec, ObjectMapper::default_service,
308         ObjectMapper::instance_path, ObjectMapper::interface,
309         ObjectMapper::method_names::get_sub_tree, subtreePath, depth,
310         interfaces);
311 
312     return ec;
313 }
314 
getDbusObject(Context::ptr ctx,const std::string & interface,const std::string & subtreePath,const std::string & match,DbusObjectInfo & dbusObject)315 boost::system::error_code getDbusObject(
316     Context::ptr ctx, const std::string& interface,
317     const std::string& subtreePath, const std::string& match,
318     DbusObjectInfo& dbusObject)
319 {
320     std::vector<DbusInterface> interfaces;
321     interfaces.emplace_back(interface);
322 
323     auto depth = 0;
324     ObjectTree objectTree;
325     boost::system::error_code ec =
326         getSubTree(ctx, interfaces, subtreePath, depth, objectTree);
327 
328     if (ec)
329     {
330         return ec;
331     }
332 
333     if (objectTree.empty())
334     {
335         lg2::error("No Object has implemented the interface: {INTERFACE}, "
336                    "NetFn: {NETFN}, Cmd: {CMD}",
337                    "INTERFACE", interface, "NETFN", lg2::hex, ctx->netFn, "CMD",
338                    lg2::hex, ctx->cmd);
339         return boost::system::errc::make_error_code(
340             boost::system::errc::no_such_process);
341     }
342 
343     // if match is empty then return the first object
344     if (match == "")
345     {
346         dbusObject = std::make_pair(
347             std::move(objectTree.begin()->first),
348             std::move(objectTree.begin()->second.begin()->first));
349         return ec;
350     }
351 
352     // else search the match string in the object path
353     auto found = std::find_if(
354         objectTree.begin(), objectTree.end(), [&match](const auto& object) {
355             return (object.first.find(match) != std::string::npos);
356         });
357 
358     if (found == objectTree.end())
359     {
360         lg2::error("Failed to find object which matches: {MATCH}, "
361                    "NetFn: {NETFN}, Cmd: {CMD}",
362                    "MATCH", match, "NETFN", lg2::hex, ctx->netFn, "CMD",
363                    lg2::hex, ctx->cmd);
364         // set ec
365         return boost::system::errc::make_error_code(
366             boost::system::errc::no_such_file_or_directory);
367     }
368 
369     dbusObject = std::make_pair(std::move(found->first),
370                                 std::move(found->second.begin()->first));
371     return ec;
372 }
373 
getAllDbusProperties(Context::ptr ctx,const std::string & service,const std::string & objPath,const std::string & interface,PropertyMap & properties)374 boost::system::error_code getAllDbusProperties(
375     Context::ptr ctx, const std::string& service, const std::string& objPath,
376     const std::string& interface, PropertyMap& properties)
377 {
378     boost::system::error_code ec;
379     properties = ctx->bus->yield_method_call<PropertyMap>(
380         ctx->yield, ec, service.c_str(), objPath.c_str(), PROP_INTF,
381         METHOD_GET_ALL, interface);
382     return ec;
383 }
384 
setDbusProperty(Context::ptr ctx,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & property,const Value & value)385 boost::system::error_code setDbusProperty(
386     Context::ptr ctx, const std::string& service, const std::string& objPath,
387     const std::string& interface, const std::string& property,
388     const Value& value)
389 {
390     boost::system::error_code ec;
391     ctx->bus->yield_method_call(ctx->yield, ec, service.c_str(),
392                                 objPath.c_str(), PROP_INTF, METHOD_SET,
393                                 interface, property, value);
394     return ec;
395 }
396 
getAllDbusObjects(Context::ptr ctx,const std::string & serviceRoot,const std::string & interface,const std::string & match,ObjectTree & objectTree)397 boost::system::error_code getAllDbusObjects(
398     Context::ptr ctx, const std::string& serviceRoot,
399     const std::string& interface, const std::string& match,
400     ObjectTree& objectTree)
401 {
402     std::vector<std::string> interfaces;
403     interfaces.emplace_back(interface);
404 
405     auto depth = 0;
406     boost::system::error_code ec =
407         getSubTree(ctx, interfaces, serviceRoot, depth, objectTree);
408     if (ec)
409     {
410         return ec;
411     }
412 
413     for (auto it = objectTree.begin(); it != objectTree.end();)
414     {
415         if (it->first.find(match) == std::string::npos)
416         {
417             it = objectTree.erase(it);
418         }
419         else
420         {
421             ++it;
422         }
423     }
424 
425     return ec;
426 }
427 
getManagedObjects(Context::ptr ctx,const std::string & service,const std::string & objPath,ObjectValueTree & objects)428 boost::system::error_code getManagedObjects(
429     Context::ptr ctx, const std::string& service, const std::string& objPath,
430     ObjectValueTree& objects)
431 {
432     boost::system::error_code ec;
433     objects = ctx->bus->yield_method_call<ipmi::ObjectValueTree>(
434         ctx->yield, ec, service.c_str(), objPath.c_str(),
435         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
436     return ec;
437 }
438 
callDbusMethod(Context::ptr ctx,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & method)439 boost::system::error_code callDbusMethod(
440     Context::ptr ctx, const std::string& service, const std::string& objPath,
441     const std::string& interface, const std::string& method)
442 {
443     boost::system::error_code ec;
444     ctx->bus->yield_method_call(ctx->yield, ec, service, objPath, interface,
445                                 method);
446     return ec;
447 }
448 
449 /********* End co-routine yielding alternatives ***************/
450 
i2cWriteRead(std::string i2cBus,const uint8_t targetAddr,std::vector<uint8_t> writeData,std::vector<uint8_t> & readBuf)451 ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t targetAddr,
452                       std::vector<uint8_t> writeData,
453                       std::vector<uint8_t>& readBuf)
454 {
455     // Open the i2c device, for low-level combined data write/read
456     int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
457     if (i2cDev < 0)
458     {
459         lg2::error("Failed to open i2c bus: {BUS}", "BUS", i2cBus);
460         return ipmi::ccInvalidFieldRequest;
461     }
462 
463     const size_t writeCount = writeData.size();
464     const size_t readCount = readBuf.size();
465     int msgCount = 0;
466     i2c_msg i2cmsg[2] = {};
467     if (writeCount)
468     {
469         // Data will be writtern to the target address
470         i2cmsg[msgCount].addr = targetAddr;
471         i2cmsg[msgCount].flags = 0x00;
472         i2cmsg[msgCount].len = writeCount;
473         i2cmsg[msgCount].buf = writeData.data();
474         msgCount++;
475     }
476     if (readCount)
477     {
478         // Data will be read into the buffer from the target address
479         i2cmsg[msgCount].addr = targetAddr;
480         i2cmsg[msgCount].flags = I2C_M_RD;
481         i2cmsg[msgCount].len = readCount;
482         i2cmsg[msgCount].buf = readBuf.data();
483         msgCount++;
484     }
485 
486     i2c_rdwr_ioctl_data msgReadWrite = {};
487     msgReadWrite.msgs = i2cmsg;
488     msgReadWrite.nmsgs = msgCount;
489 
490     // Perform the combined write/read
491     int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite);
492     ::close(i2cDev);
493 
494     if (ret < 0)
495     {
496         lg2::error("I2C WR Failed! {RET}", "RET", ret);
497         return ipmi::ccUnspecifiedError;
498     }
499     if (readCount)
500     {
501         readBuf.resize(msgReadWrite.msgs[msgCount - 1].len);
502     }
503 
504     return ipmi::ccSuccess;
505 }
506 
split(const std::string & srcStr,char delim)507 std::vector<std::string> split(const std::string& srcStr, char delim)
508 {
509     std::vector<std::string> out;
510     std::stringstream ss(srcStr);
511     std::string item;
512 
513     while (std::getline(ss, item, delim))
514     {
515         if (!item.empty())
516         {
517             out.emplace_back(item);
518         }
519     }
520     return out;
521 }
522 
523 } // namespace ipmi
524