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